0c33985ae186ca3360717d780231ad9acf7f7183
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(params.data ? params.data :this.data);
1403         }catch(e){
1404             this.fireEvent("loadexception", this, arg, null, e);
1405             callback.call(scope, null, arg, false);
1406             return;
1407         }
1408         callback.call(scope, result, arg, true);
1409     },
1410     
1411     // private
1412     update : function(params, records){
1413         
1414     }
1415 });/*
1416  * Based on:
1417  * Ext JS Library 1.1.1
1418  * Copyright(c) 2006-2007, Ext JS, LLC.
1419  *
1420  * Originally Released Under LGPL - original licence link has changed is not relivant.
1421  *
1422  * Fork - LGPL
1423  * <script type="text/javascript">
1424  */
1425 /**
1426  * @class Roo.data.HttpProxy
1427  * @extends Roo.data.DataProxy
1428  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429  * configured to reference a certain URL.<br><br>
1430  * <p>
1431  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432  * from which the running page was served.<br><br>
1433  * <p>
1434  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1435  * <p>
1436  * Be aware that to enable the browser to parse an XML document, the server must set
1437  * the Content-Type header in the HTTP response to "text/xml".
1438  * @constructor
1439  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441  * will be used to make the request.
1442  */
1443 Roo.data.HttpProxy = function(conn){
1444     Roo.data.HttpProxy.superclass.constructor.call(this);
1445     // is conn a conn config or a real conn?
1446     this.conn = conn;
1447     this.useAjax = !conn || !conn.events;
1448   
1449 };
1450
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452     // thse are take from connection...
1453     
1454     /**
1455      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1456      */
1457     /**
1458      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459      * extra parameters to each request made by this object. (defaults to undefined)
1460      */
1461     /**
1462      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463      *  to each request made by this object. (defaults to undefined)
1464      */
1465     /**
1466      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1467      */
1468     /**
1469      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1470      */
1471      /**
1472      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1473      * @type Boolean
1474      */
1475   
1476
1477     /**
1478      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1479      * @type Boolean
1480      */
1481     /**
1482      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484      * a finer-grained basis than the DataProxy events.
1485      */
1486     getConnection : function(){
1487         return this.useAjax ? Roo.Ajax : this.conn;
1488     },
1489
1490     /**
1491      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493      * process that block using the passed callback.
1494      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495      * for the request to the remote server.
1496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497      * object into a block of Roo.data.Records.
1498      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499      * The function must be passed <ul>
1500      * <li>The Record block object</li>
1501      * <li>The "arg" argument from the load function</li>
1502      * <li>A boolean success indicator</li>
1503      * </ul>
1504      * @param {Object} scope The scope in which to call the callback
1505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1506      */
1507     load : function(params, reader, callback, scope, arg){
1508         if(this.fireEvent("beforeload", this, params) !== false){
1509             var  o = {
1510                 params : params || {},
1511                 request: {
1512                     callback : callback,
1513                     scope : scope,
1514                     arg : arg
1515                 },
1516                 reader: reader,
1517                 callback : this.loadResponse,
1518                 scope: this
1519             };
1520             if(this.useAjax){
1521                 Roo.applyIf(o, this.conn);
1522                 if(this.activeRequest){
1523                     Roo.Ajax.abort(this.activeRequest);
1524                 }
1525                 this.activeRequest = Roo.Ajax.request(o);
1526             }else{
1527                 this.conn.request(o);
1528             }
1529         }else{
1530             callback.call(scope||this, null, arg, false);
1531         }
1532     },
1533
1534     // private
1535     loadResponse : function(o, success, response){
1536         delete this.activeRequest;
1537         if(!success){
1538             this.fireEvent("loadexception", this, o, response);
1539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1540             return;
1541         }
1542         var result;
1543         try {
1544             result = o.reader.read(response);
1545         }catch(e){
1546             this.fireEvent("loadexception", this, o, response, e);
1547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1548             return;
1549         }
1550         
1551         this.fireEvent("load", this, o, o.request.arg);
1552         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1553     },
1554
1555     // private
1556     update : function(dataSet){
1557
1558     },
1559
1560     // private
1561     updateResponse : function(dataSet){
1562
1563     }
1564 });/*
1565  * Based on:
1566  * Ext JS Library 1.1.1
1567  * Copyright(c) 2006-2007, Ext JS, LLC.
1568  *
1569  * Originally Released Under LGPL - original licence link has changed is not relivant.
1570  *
1571  * Fork - LGPL
1572  * <script type="text/javascript">
1573  */
1574
1575 /**
1576  * @class Roo.data.ScriptTagProxy
1577  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578  * other than the originating domain of the running page.<br><br>
1579  * <p>
1580  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1581  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1582  * <p>
1583  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584  * source code that is used as the source inside a &lt;script> tag.<br><br>
1585  * <p>
1586  * In order for the browser to process the returned data, the server must wrap the data object
1587  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589  * depending on whether the callback name was passed:
1590  * <p>
1591  * <pre><code>
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1594 if (cb != null) {
1595     scriptTag = true;
1596     response.setContentType("text/javascript");
1597 } else {
1598     response.setContentType("application/x-json");
1599 }
1600 Writer out = response.getWriter();
1601 if (scriptTag) {
1602     out.write(cb + "(");
1603 }
1604 out.print(dataBlock.toJsonString());
1605 if (scriptTag) {
1606     out.write(");");
1607 }
1608 </pre></code>
1609  *
1610  * @constructor
1611  * @param {Object} config A configuration object.
1612  */
1613 Roo.data.ScriptTagProxy = function(config){
1614     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615     Roo.apply(this, config);
1616     this.head = document.getElementsByTagName("head")[0];
1617 };
1618
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1620
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1622     /**
1623      * @cfg {String} url The URL from which to request the data object.
1624      */
1625     /**
1626      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1627      */
1628     timeout : 30000,
1629     /**
1630      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631      * the server the name of the callback function set up by the load call to process the returned data object.
1632      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633      * javascript output which calls this named function passing the data object as its only parameter.
1634      */
1635     callbackParam : "callback",
1636     /**
1637      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638      * name to the request.
1639      */
1640     nocache : true,
1641
1642     /**
1643      * Load data from the configured URL, read the data object into
1644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645      * process that block using the passed callback.
1646      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647      * for the request to the remote server.
1648      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649      * object into a block of Roo.data.Records.
1650      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651      * The function must be passed <ul>
1652      * <li>The Record block object</li>
1653      * <li>The "arg" argument from the load function</li>
1654      * <li>A boolean success indicator</li>
1655      * </ul>
1656      * @param {Object} scope The scope in which to call the callback
1657      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1658      */
1659     load : function(params, reader, callback, scope, arg){
1660         if(this.fireEvent("beforeload", this, params) !== false){
1661
1662             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1663
1664             var url = this.url;
1665             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1666             if(this.nocache){
1667                 url += "&_dc=" + (new Date().getTime());
1668             }
1669             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1670             var trans = {
1671                 id : transId,
1672                 cb : "stcCallback"+transId,
1673                 scriptId : "stcScript"+transId,
1674                 params : params,
1675                 arg : arg,
1676                 url : url,
1677                 callback : callback,
1678                 scope : scope,
1679                 reader : reader
1680             };
1681             var conn = this;
1682
1683             window[trans.cb] = function(o){
1684                 conn.handleResponse(o, trans);
1685             };
1686
1687             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1688
1689             if(this.autoAbort !== false){
1690                 this.abort();
1691             }
1692
1693             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1694
1695             var script = document.createElement("script");
1696             script.setAttribute("src", url);
1697             script.setAttribute("type", "text/javascript");
1698             script.setAttribute("id", trans.scriptId);
1699             this.head.appendChild(script);
1700
1701             this.trans = trans;
1702         }else{
1703             callback.call(scope||this, null, arg, false);
1704         }
1705     },
1706
1707     // private
1708     isLoading : function(){
1709         return this.trans ? true : false;
1710     },
1711
1712     /**
1713      * Abort the current server request.
1714      */
1715     abort : function(){
1716         if(this.isLoading()){
1717             this.destroyTrans(this.trans);
1718         }
1719     },
1720
1721     // private
1722     destroyTrans : function(trans, isLoaded){
1723         this.head.removeChild(document.getElementById(trans.scriptId));
1724         clearTimeout(trans.timeoutId);
1725         if(isLoaded){
1726             window[trans.cb] = undefined;
1727             try{
1728                 delete window[trans.cb];
1729             }catch(e){}
1730         }else{
1731             // if hasn't been loaded, wait for load to remove it to prevent script error
1732             window[trans.cb] = function(){
1733                 window[trans.cb] = undefined;
1734                 try{
1735                     delete window[trans.cb];
1736                 }catch(e){}
1737             };
1738         }
1739     },
1740
1741     // private
1742     handleResponse : function(o, trans){
1743         this.trans = false;
1744         this.destroyTrans(trans, true);
1745         var result;
1746         try {
1747             result = trans.reader.readRecords(o);
1748         }catch(e){
1749             this.fireEvent("loadexception", this, o, trans.arg, e);
1750             trans.callback.call(trans.scope||window, null, trans.arg, false);
1751             return;
1752         }
1753         this.fireEvent("load", this, o, trans.arg);
1754         trans.callback.call(trans.scope||window, result, trans.arg, true);
1755     },
1756
1757     // private
1758     handleFailure : function(trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, false);
1761         this.fireEvent("loadexception", this, null, trans.arg);
1762         trans.callback.call(trans.scope||window, null, trans.arg, false);
1763     }
1764 });/*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 /**
1776  * @class Roo.data.JsonReader
1777  * @extends Roo.data.DataReader
1778  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779  * based on mappings in a provided Roo.data.Record constructor.
1780  * 
1781  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782  * in the reply previously. 
1783  * 
1784  * <p>
1785  * Example code:
1786  * <pre><code>
1787 var RecordDef = Roo.data.Record.create([
1788     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1789     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1790 ]);
1791 var myReader = new Roo.data.JsonReader({
1792     totalProperty: "results",    // The property which contains the total dataset size (optional)
1793     root: "rows",                // The property which contains an Array of row objects
1794     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1795 }, RecordDef);
1796 </code></pre>
1797  * <p>
1798  * This would consume a JSON file like this:
1799  * <pre><code>
1800 { 'results': 2, 'rows': [
1801     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1803 }
1804 </code></pre>
1805  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807  * paged from the remote server.
1808  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809  * @cfg {String} root name of the property which contains the Array of row objects.
1810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811  * @cfg {Array} fields Array of field definition objects
1812  * @constructor
1813  * Create a new JsonReader
1814  * @param {Object} meta Metadata configuration options
1815  * @param {Object} recordType Either an Array of field definition objects,
1816  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1817  */
1818 Roo.data.JsonReader = function(meta, recordType){
1819     
1820     meta = meta || {};
1821     // set some defaults:
1822     Roo.applyIf(meta, {
1823         totalProperty: 'total',
1824         successProperty : 'success',
1825         root : 'data',
1826         id : 'id'
1827     });
1828     
1829     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1830 };
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1832     
1833     /**
1834      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1835      * Used by Store query builder to append _requestMeta to params.
1836      * 
1837      */
1838     metaFromRemote : false,
1839     /**
1840      * This method is only used by a DataProxy which has retrieved data from a remote server.
1841      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842      * @return {Object} data A data block which is used by an Roo.data.Store object as
1843      * a cache of Roo.data.Records.
1844      */
1845     read : function(response){
1846         var json = response.responseText;
1847        
1848         var o = /* eval:var:o */ eval("("+json+")");
1849         if(!o) {
1850             throw {message: "JsonReader.read: Json object not found"};
1851         }
1852         
1853         if(o.metaData){
1854             
1855             delete this.ef;
1856             this.metaFromRemote = true;
1857             this.meta = o.metaData;
1858             this.recordType = Roo.data.Record.create(o.metaData.fields);
1859             this.onMetaChange(this.meta, this.recordType, o);
1860         }
1861         return this.readRecords(o);
1862     },
1863
1864     // private function a store will implement
1865     onMetaChange : function(meta, recordType, o){
1866
1867     },
1868
1869     /**
1870          * @ignore
1871          */
1872     simpleAccess: function(obj, subsc) {
1873         return obj[subsc];
1874     },
1875
1876         /**
1877          * @ignore
1878          */
1879     getJsonAccessor: function(){
1880         var re = /[\[\.]/;
1881         return function(expr) {
1882             try {
1883                 return(re.test(expr))
1884                     ? new Function("obj", "return obj." + expr)
1885                     : function(obj){
1886                         return obj[expr];
1887                     };
1888             } catch(e){}
1889             return Roo.emptyFn;
1890         };
1891     }(),
1892
1893     /**
1894      * Create a data block containing Roo.data.Records from an XML document.
1895      * @param {Object} o An object which contains an Array of row objects in the property specified
1896      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897      * which contains the total size of the dataset.
1898      * @return {Object} data A data block which is used by an Roo.data.Store object as
1899      * a cache of Roo.data.Records.
1900      */
1901     readRecords : function(o){
1902         /**
1903          * After any data loads, the raw JSON data is available for further custom processing.
1904          * @type Object
1905          */
1906         this.o = o;
1907         var s = this.meta, Record = this.recordType,
1908             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1909
1910 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1911         if (!this.ef) {
1912             if(s.totalProperty) {
1913                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1914                 }
1915                 if(s.successProperty) {
1916                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1917                 }
1918                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1919                 if (s.id) {
1920                         var g = this.getJsonAccessor(s.id);
1921                         this.getId = function(rec) {
1922                                 var r = g(rec);  
1923                                 return (r === undefined || r === "") ? null : r;
1924                         };
1925                 } else {
1926                         this.getId = function(){return null;};
1927                 }
1928             this.ef = [];
1929             for(var jj = 0; jj < fl; jj++){
1930                 f = fi[jj];
1931                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932                 this.ef[jj] = this.getJsonAccessor(map);
1933             }
1934         }
1935
1936         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937         if(s.totalProperty){
1938             var vt = parseInt(this.getTotal(o), 10);
1939             if(!isNaN(vt)){
1940                 totalRecords = vt;
1941             }
1942         }
1943         if(s.successProperty){
1944             var vs = this.getSuccess(o);
1945             if(vs === false || vs === 'false'){
1946                 success = false;
1947             }
1948         }
1949         var records = [];
1950         for(var i = 0; i < c; i++){
1951                 var n = root[i];
1952             var values = {};
1953             var id = this.getId(n);
1954             for(var j = 0; j < fl; j++){
1955                 f = fi[j];
1956             var v = this.ef[j](n);
1957             if (!f.convert) {
1958                 Roo.log('missing convert for ' + f.name);
1959                 Roo.log(f);
1960                 continue;
1961             }
1962             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1963             }
1964             var record = new Record(values, id);
1965             record.json = n;
1966             records[i] = record;
1967         }
1968         return {
1969             raw : o,
1970             success : success,
1971             records : records,
1972             totalRecords : totalRecords
1973         };
1974     }
1975 });/*
1976  * Based on:
1977  * Ext JS Library 1.1.1
1978  * Copyright(c) 2006-2007, Ext JS, LLC.
1979  *
1980  * Originally Released Under LGPL - original licence link has changed is not relivant.
1981  *
1982  * Fork - LGPL
1983  * <script type="text/javascript">
1984  */
1985
1986 /**
1987  * @class Roo.data.XmlReader
1988  * @extends Roo.data.DataReader
1989  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1991  * <p>
1992  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993  * header in the HTTP response must be set to "text/xml".</em>
1994  * <p>
1995  * Example code:
1996  * <pre><code>
1997 var RecordDef = Roo.data.Record.create([
1998    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1999    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2000 ]);
2001 var myReader = new Roo.data.XmlReader({
2002    totalRecords: "results", // The element which contains the total dataset size (optional)
2003    record: "row",           // The repeated element which contains row information
2004    id: "id"                 // The element within the row that provides an ID for the record (optional)
2005 }, RecordDef);
2006 </code></pre>
2007  * <p>
2008  * This would consume an XML file like this:
2009  * <pre><code>
2010 &lt;?xml?>
2011 &lt;dataset>
2012  &lt;results>2&lt;/results>
2013  &lt;row>
2014    &lt;id>1&lt;/id>
2015    &lt;name>Bill&lt;/name>
2016    &lt;occupation>Gardener&lt;/occupation>
2017  &lt;/row>
2018  &lt;row>
2019    &lt;id>2&lt;/id>
2020    &lt;name>Ben&lt;/name>
2021    &lt;occupation>Horticulturalist&lt;/occupation>
2022  &lt;/row>
2023 &lt;/dataset>
2024 </code></pre>
2025  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027  * paged from the remote server.
2028  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031  * a record identifier value.
2032  * @constructor
2033  * Create a new XmlReader
2034  * @param {Object} meta Metadata configuration options
2035  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2036  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2038  */
2039 Roo.data.XmlReader = function(meta, recordType){
2040     meta = meta || {};
2041     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2042 };
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2044     /**
2045      * This method is only used by a DataProxy which has retrieved data from a remote server.
2046          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2047          * to contain a method called 'responseXML' that returns an XML document object.
2048      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049      * a cache of Roo.data.Records.
2050      */
2051     read : function(response){
2052         var doc = response.responseXML;
2053         if(!doc) {
2054             throw {message: "XmlReader.read: XML Document not available"};
2055         }
2056         return this.readRecords(doc);
2057     },
2058
2059     /**
2060      * Create a data block containing Roo.data.Records from an XML document.
2061          * @param {Object} doc A parsed XML document.
2062      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063      * a cache of Roo.data.Records.
2064      */
2065     readRecords : function(doc){
2066         /**
2067          * After any data loads/reads, the raw XML Document is available for further custom processing.
2068          * @type XMLDocument
2069          */
2070         this.xmlData = doc;
2071         var root = doc.documentElement || doc;
2072         var q = Roo.DomQuery;
2073         var recordType = this.recordType, fields = recordType.prototype.fields;
2074         var sid = this.meta.id;
2075         var totalRecords = 0, success = true;
2076         if(this.meta.totalRecords){
2077             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2078         }
2079         
2080         if(this.meta.success){
2081             var sv = q.selectValue(this.meta.success, root, true);
2082             success = sv !== false && sv !== 'false';
2083         }
2084         var records = [];
2085         var ns = q.select(this.meta.record, root);
2086         for(var i = 0, len = ns.length; i < len; i++) {
2087                 var n = ns[i];
2088                 var values = {};
2089                 var id = sid ? q.selectValue(sid, n) : undefined;
2090                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091                     var f = fields.items[j];
2092                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2093                     v = f.convert(v);
2094                     values[f.name] = v;
2095                 }
2096                 var record = new recordType(values, id);
2097                 record.node = n;
2098                 records[records.length] = record;
2099             }
2100
2101             return {
2102                 success : success,
2103                 records : records,
2104                 totalRecords : totalRecords || records.length
2105             };
2106     }
2107 });/*
2108  * Based on:
2109  * Ext JS Library 1.1.1
2110  * Copyright(c) 2006-2007, Ext JS, LLC.
2111  *
2112  * Originally Released Under LGPL - original licence link has changed is not relivant.
2113  *
2114  * Fork - LGPL
2115  * <script type="text/javascript">
2116  */
2117
2118 /**
2119  * @class Roo.data.ArrayReader
2120  * @extends Roo.data.DataReader
2121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122  * Each element of that Array represents a row of data fields. The
2123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2125  * <p>
2126  * Example code:.
2127  * <pre><code>
2128 var RecordDef = Roo.data.Record.create([
2129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2131 ]);
2132 var myReader = new Roo.data.ArrayReader({
2133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2134 }, RecordDef);
2135 </code></pre>
2136  * <p>
2137  * This would consume an Array like this:
2138  * <pre><code>
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2140   </code></pre>
2141  
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object|Array} recordType Either an Array of field definition objects
2146  * 
2147  * @cfg {Array} fields Array of field definition objects
2148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2149  * as specified to {@link Roo.data.Record#create},
2150  * or an {@link Roo.data.Record} object
2151  *
2152  * 
2153  * created using {@link Roo.data.Record#create}.
2154  */
2155 Roo.data.ArrayReader = function(meta, recordType){
2156     
2157      
2158     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2159 };
2160
2161 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2162     /**
2163      * Create a data block containing Roo.data.Records from an XML document.
2164      * @param {Object} o An Array of row objects which represents the dataset.
2165      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2166      * a cache of Roo.data.Records.
2167      */
2168     readRecords : function(o){
2169         var sid = this.meta ? this.meta.id : null;
2170         var recordType = this.recordType, fields = recordType.prototype.fields;
2171         var records = [];
2172         var root = o;
2173             for(var i = 0; i < root.length; i++){
2174                     var n = root[i];
2175                 var values = {};
2176                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2177                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2178                 var f = fields.items[j];
2179                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2180                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2181                 v = f.convert(v);
2182                 values[f.name] = v;
2183             }
2184                 var record = new recordType(values, id);
2185                 record.json = n;
2186                 records[records.length] = record;
2187             }
2188             return {
2189                 records : records,
2190                 totalRecords : records.length
2191             };
2192     }
2193 });/*
2194  * Based on:
2195  * Ext JS Library 1.1.1
2196  * Copyright(c) 2006-2007, Ext JS, LLC.
2197  *
2198  * Originally Released Under LGPL - original licence link has changed is not relivant.
2199  *
2200  * Fork - LGPL
2201  * <script type="text/javascript">
2202  */
2203
2204
2205 /**
2206  * @class Roo.data.Tree
2207  * @extends Roo.util.Observable
2208  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2209  * in the tree have most standard DOM functionality.
2210  * @constructor
2211  * @param {Node} root (optional) The root node
2212  */
2213 Roo.data.Tree = function(root){
2214    this.nodeHash = {};
2215    /**
2216     * The root node for this tree
2217     * @type Node
2218     */
2219    this.root = null;
2220    if(root){
2221        this.setRootNode(root);
2222    }
2223    this.addEvents({
2224        /**
2225         * @event append
2226         * Fires when a new child node is appended to a node in this tree.
2227         * @param {Tree} tree The owner tree
2228         * @param {Node} parent The parent node
2229         * @param {Node} node The newly appended node
2230         * @param {Number} index The index of the newly appended node
2231         */
2232        "append" : true,
2233        /**
2234         * @event remove
2235         * Fires when a child node is removed from a node in this tree.
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} parent The parent node
2238         * @param {Node} node The child node removed
2239         */
2240        "remove" : true,
2241        /**
2242         * @event move
2243         * Fires when a node is moved to a new location in the tree
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} node The node moved
2246         * @param {Node} oldParent The old parent of this node
2247         * @param {Node} newParent The new parent of this node
2248         * @param {Number} index The index it was moved to
2249         */
2250        "move" : true,
2251        /**
2252         * @event insert
2253         * Fires when a new child node is inserted in a node in this tree.
2254         * @param {Tree} tree The owner tree
2255         * @param {Node} parent The parent node
2256         * @param {Node} node The child node inserted
2257         * @param {Node} refNode The child node the node was inserted before
2258         */
2259        "insert" : true,
2260        /**
2261         * @event beforeappend
2262         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be appended
2266         */
2267        "beforeappend" : true,
2268        /**
2269         * @event beforeremove
2270         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node to be removed
2274         */
2275        "beforeremove" : true,
2276        /**
2277         * @event beforemove
2278         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} node The node being moved
2281         * @param {Node} oldParent The parent of the node
2282         * @param {Node} newParent The new parent the node is moving to
2283         * @param {Number} index The index it is being moved to
2284         */
2285        "beforemove" : true,
2286        /**
2287         * @event beforeinsert
2288         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} parent The parent node
2291         * @param {Node} node The child node to be inserted
2292         * @param {Node} refNode The child node the node is being inserted before
2293         */
2294        "beforeinsert" : true
2295    });
2296
2297     Roo.data.Tree.superclass.constructor.call(this);
2298 };
2299
2300 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2301     pathSeparator: "/",
2302
2303     proxyNodeEvent : function(){
2304         return this.fireEvent.apply(this, arguments);
2305     },
2306
2307     /**
2308      * Returns the root node for this tree.
2309      * @return {Node}
2310      */
2311     getRootNode : function(){
2312         return this.root;
2313     },
2314
2315     /**
2316      * Sets the root node for this tree.
2317      * @param {Node} node
2318      * @return {Node}
2319      */
2320     setRootNode : function(node){
2321         this.root = node;
2322         node.ownerTree = this;
2323         node.isRoot = true;
2324         this.registerNode(node);
2325         return node;
2326     },
2327
2328     /**
2329      * Gets a node in this tree by its id.
2330      * @param {String} id
2331      * @return {Node}
2332      */
2333     getNodeById : function(id){
2334         return this.nodeHash[id];
2335     },
2336
2337     registerNode : function(node){
2338         this.nodeHash[node.id] = node;
2339     },
2340
2341     unregisterNode : function(node){
2342         delete this.nodeHash[node.id];
2343     },
2344
2345     toString : function(){
2346         return "[Tree"+(this.id?" "+this.id:"")+"]";
2347     }
2348 });
2349
2350 /**
2351  * @class Roo.data.Node
2352  * @extends Roo.util.Observable
2353  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2354  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2355  * @constructor
2356  * @param {Object} attributes The attributes/config for the node
2357  */
2358 Roo.data.Node = function(attributes){
2359     /**
2360      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2361      * @type {Object}
2362      */
2363     this.attributes = attributes || {};
2364     this.leaf = this.attributes.leaf;
2365     /**
2366      * The node id. @type String
2367      */
2368     this.id = this.attributes.id;
2369     if(!this.id){
2370         this.id = Roo.id(null, "ynode-");
2371         this.attributes.id = this.id;
2372     }
2373      
2374     
2375     /**
2376      * All child nodes of this node. @type Array
2377      */
2378     this.childNodes = [];
2379     if(!this.childNodes.indexOf){ // indexOf is a must
2380         this.childNodes.indexOf = function(o){
2381             for(var i = 0, len = this.length; i < len; i++){
2382                 if(this[i] == o) {
2383                     return i;
2384                 }
2385             }
2386             return -1;
2387         };
2388     }
2389     /**
2390      * The parent node for this node. @type Node
2391      */
2392     this.parentNode = null;
2393     /**
2394      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2395      */
2396     this.firstChild = null;
2397     /**
2398      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2399      */
2400     this.lastChild = null;
2401     /**
2402      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2403      */
2404     this.previousSibling = null;
2405     /**
2406      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2407      */
2408     this.nextSibling = null;
2409
2410     this.addEvents({
2411        /**
2412         * @event append
2413         * Fires when a new child node is appended
2414         * @param {Tree} tree The owner tree
2415         * @param {Node} this This node
2416         * @param {Node} node The newly appended node
2417         * @param {Number} index The index of the newly appended node
2418         */
2419        "append" : true,
2420        /**
2421         * @event remove
2422         * Fires when a child node is removed
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} node The removed node
2426         */
2427        "remove" : true,
2428        /**
2429         * @event move
2430         * Fires when this node is moved to a new location in the tree
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} oldParent The old parent of this node
2434         * @param {Node} newParent The new parent of this node
2435         * @param {Number} index The index it was moved to
2436         */
2437        "move" : true,
2438        /**
2439         * @event insert
2440         * Fires when a new child node is inserted.
2441         * @param {Tree} tree The owner tree
2442         * @param {Node} this This node
2443         * @param {Node} node The child node inserted
2444         * @param {Node} refNode The child node the node was inserted before
2445         */
2446        "insert" : true,
2447        /**
2448         * @event beforeappend
2449         * Fires before a new child is appended, return false to cancel the append.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be appended
2453         */
2454        "beforeappend" : true,
2455        /**
2456         * @event beforeremove
2457         * Fires before a child is removed, return false to cancel the remove.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The child node to be removed
2461         */
2462        "beforeremove" : true,
2463        /**
2464         * @event beforemove
2465         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} oldParent The parent of this node
2469         * @param {Node} newParent The new parent this node is moving to
2470         * @param {Number} index The index it is being moved to
2471         */
2472        "beforemove" : true,
2473        /**
2474         * @event beforeinsert
2475         * Fires before a new child is inserted, return false to cancel the insert.
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} node The child node to be inserted
2479         * @param {Node} refNode The child node the node is being inserted before
2480         */
2481        "beforeinsert" : true
2482    });
2483     this.listeners = this.attributes.listeners;
2484     Roo.data.Node.superclass.constructor.call(this);
2485 };
2486
2487 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2488     fireEvent : function(evtName){
2489         // first do standard event for this node
2490         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2491             return false;
2492         }
2493         // then bubble it up to the tree if the event wasn't cancelled
2494         var ot = this.getOwnerTree();
2495         if(ot){
2496             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2497                 return false;
2498             }
2499         }
2500         return true;
2501     },
2502
2503     /**
2504      * Returns true if this node is a leaf
2505      * @return {Boolean}
2506      */
2507     isLeaf : function(){
2508         return this.leaf === true;
2509     },
2510
2511     // private
2512     setFirstChild : function(node){
2513         this.firstChild = node;
2514     },
2515
2516     //private
2517     setLastChild : function(node){
2518         this.lastChild = node;
2519     },
2520
2521
2522     /**
2523      * Returns true if this node is the last child of its parent
2524      * @return {Boolean}
2525      */
2526     isLast : function(){
2527        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2528     },
2529
2530     /**
2531      * Returns true if this node is the first child of its parent
2532      * @return {Boolean}
2533      */
2534     isFirst : function(){
2535        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2536     },
2537
2538     hasChildNodes : function(){
2539         return !this.isLeaf() && this.childNodes.length > 0;
2540     },
2541
2542     /**
2543      * Insert node(s) as the last child node of this node.
2544      * @param {Node/Array} node The node or Array of nodes to append
2545      * @return {Node} The appended node if single append, or null if an array was passed
2546      */
2547     appendChild : function(node){
2548         var multi = false;
2549         if(node instanceof Array){
2550             multi = node;
2551         }else if(arguments.length > 1){
2552             multi = arguments;
2553         }
2554         // if passed an array or multiple args do them one by one
2555         if(multi){
2556             for(var i = 0, len = multi.length; i < len; i++) {
2557                 this.appendChild(multi[i]);
2558             }
2559         }else{
2560             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2561                 return false;
2562             }
2563             var index = this.childNodes.length;
2564             var oldParent = node.parentNode;
2565             // it's a move, make sure we move it cleanly
2566             if(oldParent){
2567                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2568                     return false;
2569                 }
2570                 oldParent.removeChild(node);
2571             }
2572             index = this.childNodes.length;
2573             if(index == 0){
2574                 this.setFirstChild(node);
2575             }
2576             this.childNodes.push(node);
2577             node.parentNode = this;
2578             var ps = this.childNodes[index-1];
2579             if(ps){
2580                 node.previousSibling = ps;
2581                 ps.nextSibling = node;
2582             }else{
2583                 node.previousSibling = null;
2584             }
2585             node.nextSibling = null;
2586             this.setLastChild(node);
2587             node.setOwnerTree(this.getOwnerTree());
2588             this.fireEvent("append", this.ownerTree, this, node, index);
2589             if(oldParent){
2590                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2591             }
2592             return node;
2593         }
2594     },
2595
2596     /**
2597      * Removes a child node from this node.
2598      * @param {Node} node The node to remove
2599      * @return {Node} The removed node
2600      */
2601     removeChild : function(node){
2602         var index = this.childNodes.indexOf(node);
2603         if(index == -1){
2604             return false;
2605         }
2606         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2607             return false;
2608         }
2609
2610         // remove it from childNodes collection
2611         this.childNodes.splice(index, 1);
2612
2613         // update siblings
2614         if(node.previousSibling){
2615             node.previousSibling.nextSibling = node.nextSibling;
2616         }
2617         if(node.nextSibling){
2618             node.nextSibling.previousSibling = node.previousSibling;
2619         }
2620
2621         // update child refs
2622         if(this.firstChild == node){
2623             this.setFirstChild(node.nextSibling);
2624         }
2625         if(this.lastChild == node){
2626             this.setLastChild(node.previousSibling);
2627         }
2628
2629         node.setOwnerTree(null);
2630         // clear any references from the node
2631         node.parentNode = null;
2632         node.previousSibling = null;
2633         node.nextSibling = null;
2634         this.fireEvent("remove", this.ownerTree, this, node);
2635         return node;
2636     },
2637
2638     /**
2639      * Inserts the first node before the second node in this nodes childNodes collection.
2640      * @param {Node} node The node to insert
2641      * @param {Node} refNode The node to insert before (if null the node is appended)
2642      * @return {Node} The inserted node
2643      */
2644     insertBefore : function(node, refNode){
2645         if(!refNode){ // like standard Dom, refNode can be null for append
2646             return this.appendChild(node);
2647         }
2648         // nothing to do
2649         if(node == refNode){
2650             return false;
2651         }
2652
2653         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2654             return false;
2655         }
2656         var index = this.childNodes.indexOf(refNode);
2657         var oldParent = node.parentNode;
2658         var refIndex = index;
2659
2660         // when moving internally, indexes will change after remove
2661         if(oldParent == this && this.childNodes.indexOf(node) < index){
2662             refIndex--;
2663         }
2664
2665         // it's a move, make sure we move it cleanly
2666         if(oldParent){
2667             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2668                 return false;
2669             }
2670             oldParent.removeChild(node);
2671         }
2672         if(refIndex == 0){
2673             this.setFirstChild(node);
2674         }
2675         this.childNodes.splice(refIndex, 0, node);
2676         node.parentNode = this;
2677         var ps = this.childNodes[refIndex-1];
2678         if(ps){
2679             node.previousSibling = ps;
2680             ps.nextSibling = node;
2681         }else{
2682             node.previousSibling = null;
2683         }
2684         node.nextSibling = refNode;
2685         refNode.previousSibling = node;
2686         node.setOwnerTree(this.getOwnerTree());
2687         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2688         if(oldParent){
2689             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2690         }
2691         return node;
2692     },
2693
2694     /**
2695      * Returns the child node at the specified index.
2696      * @param {Number} index
2697      * @return {Node}
2698      */
2699     item : function(index){
2700         return this.childNodes[index];
2701     },
2702
2703     /**
2704      * Replaces one child node in this node with another.
2705      * @param {Node} newChild The replacement node
2706      * @param {Node} oldChild The node to replace
2707      * @return {Node} The replaced node
2708      */
2709     replaceChild : function(newChild, oldChild){
2710         this.insertBefore(newChild, oldChild);
2711         this.removeChild(oldChild);
2712         return oldChild;
2713     },
2714
2715     /**
2716      * Returns the index of a child node
2717      * @param {Node} node
2718      * @return {Number} The index of the node or -1 if it was not found
2719      */
2720     indexOf : function(child){
2721         return this.childNodes.indexOf(child);
2722     },
2723
2724     /**
2725      * Returns the tree this node is in.
2726      * @return {Tree}
2727      */
2728     getOwnerTree : function(){
2729         // if it doesn't have one, look for one
2730         if(!this.ownerTree){
2731             var p = this;
2732             while(p){
2733                 if(p.ownerTree){
2734                     this.ownerTree = p.ownerTree;
2735                     break;
2736                 }
2737                 p = p.parentNode;
2738             }
2739         }
2740         return this.ownerTree;
2741     },
2742
2743     /**
2744      * Returns depth of this node (the root node has a depth of 0)
2745      * @return {Number}
2746      */
2747     getDepth : function(){
2748         var depth = 0;
2749         var p = this;
2750         while(p.parentNode){
2751             ++depth;
2752             p = p.parentNode;
2753         }
2754         return depth;
2755     },
2756
2757     // private
2758     setOwnerTree : function(tree){
2759         // if it's move, we need to update everyone
2760         if(tree != this.ownerTree){
2761             if(this.ownerTree){
2762                 this.ownerTree.unregisterNode(this);
2763             }
2764             this.ownerTree = tree;
2765             var cs = this.childNodes;
2766             for(var i = 0, len = cs.length; i < len; i++) {
2767                 cs[i].setOwnerTree(tree);
2768             }
2769             if(tree){
2770                 tree.registerNode(this);
2771             }
2772         }
2773     },
2774
2775     /**
2776      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2777      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2778      * @return {String} The path
2779      */
2780     getPath : function(attr){
2781         attr = attr || "id";
2782         var p = this.parentNode;
2783         var b = [this.attributes[attr]];
2784         while(p){
2785             b.unshift(p.attributes[attr]);
2786             p = p.parentNode;
2787         }
2788         var sep = this.getOwnerTree().pathSeparator;
2789         return sep + b.join(sep);
2790     },
2791
2792     /**
2793      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794      * function call will be the scope provided or the current node. The arguments to the function
2795      * will be the args provided or the current node. If the function returns false at any point,
2796      * the bubble is stopped.
2797      * @param {Function} fn The function to call
2798      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2800      */
2801     bubble : function(fn, scope, args){
2802         var p = this;
2803         while(p){
2804             if(fn.call(scope || p, args || p) === false){
2805                 break;
2806             }
2807             p = p.parentNode;
2808         }
2809     },
2810
2811     /**
2812      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2813      * function call will be the scope provided or the current node. The arguments to the function
2814      * will be the args provided or the current node. If the function returns false at any point,
2815      * the cascade is stopped on that branch.
2816      * @param {Function} fn The function to call
2817      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2818      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819      */
2820     cascade : function(fn, scope, args){
2821         if(fn.call(scope || this, args || this) !== false){
2822             var cs = this.childNodes;
2823             for(var i = 0, len = cs.length; i < len; i++) {
2824                 cs[i].cascade(fn, scope, args);
2825             }
2826         }
2827     },
2828
2829     /**
2830      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the iteration stops.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     eachChild : function(fn, scope, args){
2839         var cs = this.childNodes;
2840         for(var i = 0, len = cs.length; i < len; i++) {
2841                 if(fn.call(scope || this, args || cs[i]) === false){
2842                     break;
2843                 }
2844         }
2845     },
2846
2847     /**
2848      * Finds the first child that has the attribute with the specified value.
2849      * @param {String} attribute The attribute name
2850      * @param {Mixed} value The value to search for
2851      * @return {Node} The found child or null if none was found
2852      */
2853     findChild : function(attribute, value){
2854         var cs = this.childNodes;
2855         for(var i = 0, len = cs.length; i < len; i++) {
2856                 if(cs[i].attributes[attribute] == value){
2857                     return cs[i];
2858                 }
2859         }
2860         return null;
2861     },
2862
2863     /**
2864      * Finds the first child by a custom function. The child matches if the function passed
2865      * returns true.
2866      * @param {Function} fn
2867      * @param {Object} scope (optional)
2868      * @return {Node} The found child or null if none was found
2869      */
2870     findChildBy : function(fn, scope){
2871         var cs = this.childNodes;
2872         for(var i = 0, len = cs.length; i < len; i++) {
2873                 if(fn.call(scope||cs[i], cs[i]) === true){
2874                     return cs[i];
2875                 }
2876         }
2877         return null;
2878     },
2879
2880     /**
2881      * Sorts this nodes children using the supplied sort function
2882      * @param {Function} fn
2883      * @param {Object} scope (optional)
2884      */
2885     sort : function(fn, scope){
2886         var cs = this.childNodes;
2887         var len = cs.length;
2888         if(len > 0){
2889             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2890             cs.sort(sortFn);
2891             for(var i = 0; i < len; i++){
2892                 var n = cs[i];
2893                 n.previousSibling = cs[i-1];
2894                 n.nextSibling = cs[i+1];
2895                 if(i == 0){
2896                     this.setFirstChild(n);
2897                 }
2898                 if(i == len-1){
2899                     this.setLastChild(n);
2900                 }
2901             }
2902         }
2903     },
2904
2905     /**
2906      * Returns true if this node is an ancestor (at any point) of the passed node.
2907      * @param {Node} node
2908      * @return {Boolean}
2909      */
2910     contains : function(node){
2911         return node.isAncestor(this);
2912     },
2913
2914     /**
2915      * Returns true if the passed node is an ancestor (at any point) of this node.
2916      * @param {Node} node
2917      * @return {Boolean}
2918      */
2919     isAncestor : function(node){
2920         var p = this.parentNode;
2921         while(p){
2922             if(p == node){
2923                 return true;
2924             }
2925             p = p.parentNode;
2926         }
2927         return false;
2928     },
2929
2930     toString : function(){
2931         return "[Node"+(this.id?" "+this.id:"")+"]";
2932     }
2933 });/*
2934  * Based on:
2935  * Ext JS Library 1.1.1
2936  * Copyright(c) 2006-2007, Ext JS, LLC.
2937  *
2938  * Originally Released Under LGPL - original licence link has changed is not relivant.
2939  *
2940  * Fork - LGPL
2941  * <script type="text/javascript">
2942  */
2943  (function(){ 
2944 /**
2945  * @class Roo.Layer
2946  * @extends Roo.Element
2947  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2948  * automatic maintaining of shadow/shim positions.
2949  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2950  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2951  * you can pass a string with a CSS class name. False turns off the shadow.
2952  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2953  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2954  * @cfg {String} cls CSS class to add to the element
2955  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2956  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2957  * @constructor
2958  * @param {Object} config An object with config options.
2959  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2960  */
2961
2962 Roo.Layer = function(config, existingEl){
2963     config = config || {};
2964     var dh = Roo.DomHelper;
2965     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2966     if(existingEl){
2967         this.dom = Roo.getDom(existingEl);
2968     }
2969     if(!this.dom){
2970         var o = config.dh || {tag: "div", cls: "x-layer"};
2971         this.dom = dh.append(pel, o);
2972     }
2973     if(config.cls){
2974         this.addClass(config.cls);
2975     }
2976     this.constrain = config.constrain !== false;
2977     this.visibilityMode = Roo.Element.VISIBILITY;
2978     if(config.id){
2979         this.id = this.dom.id = config.id;
2980     }else{
2981         this.id = Roo.id(this.dom);
2982     }
2983     this.zindex = config.zindex || this.getZIndex();
2984     this.position("absolute", this.zindex);
2985     if(config.shadow){
2986         this.shadowOffset = config.shadowOffset || 4;
2987         this.shadow = new Roo.Shadow({
2988             offset : this.shadowOffset,
2989             mode : config.shadow
2990         });
2991     }else{
2992         this.shadowOffset = 0;
2993     }
2994     this.useShim = config.shim !== false && Roo.useShims;
2995     this.useDisplay = config.useDisplay;
2996     this.hide();
2997 };
2998
2999 var supr = Roo.Element.prototype;
3000
3001 // shims are shared among layer to keep from having 100 iframes
3002 var shims = [];
3003
3004 Roo.extend(Roo.Layer, Roo.Element, {
3005
3006     getZIndex : function(){
3007         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3008     },
3009
3010     getShim : function(){
3011         if(!this.useShim){
3012             return null;
3013         }
3014         if(this.shim){
3015             return this.shim;
3016         }
3017         var shim = shims.shift();
3018         if(!shim){
3019             shim = this.createShim();
3020             shim.enableDisplayMode('block');
3021             shim.dom.style.display = 'none';
3022             shim.dom.style.visibility = 'visible';
3023         }
3024         var pn = this.dom.parentNode;
3025         if(shim.dom.parentNode != pn){
3026             pn.insertBefore(shim.dom, this.dom);
3027         }
3028         shim.setStyle('z-index', this.getZIndex()-2);
3029         this.shim = shim;
3030         return shim;
3031     },
3032
3033     hideShim : function(){
3034         if(this.shim){
3035             this.shim.setDisplayed(false);
3036             shims.push(this.shim);
3037             delete this.shim;
3038         }
3039     },
3040
3041     disableShadow : function(){
3042         if(this.shadow){
3043             this.shadowDisabled = true;
3044             this.shadow.hide();
3045             this.lastShadowOffset = this.shadowOffset;
3046             this.shadowOffset = 0;
3047         }
3048     },
3049
3050     enableShadow : function(show){
3051         if(this.shadow){
3052             this.shadowDisabled = false;
3053             this.shadowOffset = this.lastShadowOffset;
3054             delete this.lastShadowOffset;
3055             if(show){
3056                 this.sync(true);
3057             }
3058         }
3059     },
3060
3061     // private
3062     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3063     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3064     sync : function(doShow){
3065         var sw = this.shadow;
3066         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3067             var sh = this.getShim();
3068
3069             var w = this.getWidth(),
3070                 h = this.getHeight();
3071
3072             var l = this.getLeft(true),
3073                 t = this.getTop(true);
3074
3075             if(sw && !this.shadowDisabled){
3076                 if(doShow && !sw.isVisible()){
3077                     sw.show(this);
3078                 }else{
3079                     sw.realign(l, t, w, h);
3080                 }
3081                 if(sh){
3082                     if(doShow){
3083                        sh.show();
3084                     }
3085                     // fit the shim behind the shadow, so it is shimmed too
3086                     var a = sw.adjusts, s = sh.dom.style;
3087                     s.left = (Math.min(l, l+a.l))+"px";
3088                     s.top = (Math.min(t, t+a.t))+"px";
3089                     s.width = (w+a.w)+"px";
3090                     s.height = (h+a.h)+"px";
3091                 }
3092             }else if(sh){
3093                 if(doShow){
3094                    sh.show();
3095                 }
3096                 sh.setSize(w, h);
3097                 sh.setLeftTop(l, t);
3098             }
3099             
3100         }
3101     },
3102
3103     // private
3104     destroy : function(){
3105         this.hideShim();
3106         if(this.shadow){
3107             this.shadow.hide();
3108         }
3109         this.removeAllListeners();
3110         var pn = this.dom.parentNode;
3111         if(pn){
3112             pn.removeChild(this.dom);
3113         }
3114         Roo.Element.uncache(this.id);
3115     },
3116
3117     remove : function(){
3118         this.destroy();
3119     },
3120
3121     // private
3122     beginUpdate : function(){
3123         this.updating = true;
3124     },
3125
3126     // private
3127     endUpdate : function(){
3128         this.updating = false;
3129         this.sync(true);
3130     },
3131
3132     // private
3133     hideUnders : function(negOffset){
3134         if(this.shadow){
3135             this.shadow.hide();
3136         }
3137         this.hideShim();
3138     },
3139
3140     // private
3141     constrainXY : function(){
3142         if(this.constrain){
3143             var vw = Roo.lib.Dom.getViewWidth(),
3144                 vh = Roo.lib.Dom.getViewHeight();
3145             var s = Roo.get(document).getScroll();
3146
3147             var xy = this.getXY();
3148             var x = xy[0], y = xy[1];   
3149             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3150             // only move it if it needs it
3151             var moved = false;
3152             // first validate right/bottom
3153             if((x + w) > vw+s.left){
3154                 x = vw - w - this.shadowOffset;
3155                 moved = true;
3156             }
3157             if((y + h) > vh+s.top){
3158                 y = vh - h - this.shadowOffset;
3159                 moved = true;
3160             }
3161             // then make sure top/left isn't negative
3162             if(x < s.left){
3163                 x = s.left;
3164                 moved = true;
3165             }
3166             if(y < s.top){
3167                 y = s.top;
3168                 moved = true;
3169             }
3170             if(moved){
3171                 if(this.avoidY){
3172                     var ay = this.avoidY;
3173                     if(y <= ay && (y+h) >= ay){
3174                         y = ay-h-5;   
3175                     }
3176                 }
3177                 xy = [x, y];
3178                 this.storeXY(xy);
3179                 supr.setXY.call(this, xy);
3180                 this.sync();
3181             }
3182         }
3183     },
3184
3185     isVisible : function(){
3186         return this.visible;    
3187     },
3188
3189     // private
3190     showAction : function(){
3191         this.visible = true; // track visibility to prevent getStyle calls
3192         if(this.useDisplay === true){
3193             this.setDisplayed("");
3194         }else if(this.lastXY){
3195             supr.setXY.call(this, this.lastXY);
3196         }else if(this.lastLT){
3197             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3198         }
3199     },
3200
3201     // private
3202     hideAction : function(){
3203         this.visible = false;
3204         if(this.useDisplay === true){
3205             this.setDisplayed(false);
3206         }else{
3207             this.setLeftTop(-10000,-10000);
3208         }
3209     },
3210
3211     // overridden Element method
3212     setVisible : function(v, a, d, c, e){
3213         if(v){
3214             this.showAction();
3215         }
3216         if(a && v){
3217             var cb = function(){
3218                 this.sync(true);
3219                 if(c){
3220                     c();
3221                 }
3222             }.createDelegate(this);
3223             supr.setVisible.call(this, true, true, d, cb, e);
3224         }else{
3225             if(!v){
3226                 this.hideUnders(true);
3227             }
3228             var cb = c;
3229             if(a){
3230                 cb = function(){
3231                     this.hideAction();
3232                     if(c){
3233                         c();
3234                     }
3235                 }.createDelegate(this);
3236             }
3237             supr.setVisible.call(this, v, a, d, cb, e);
3238             if(v){
3239                 this.sync(true);
3240             }else if(!a){
3241                 this.hideAction();
3242             }
3243         }
3244     },
3245
3246     storeXY : function(xy){
3247         delete this.lastLT;
3248         this.lastXY = xy;
3249     },
3250
3251     storeLeftTop : function(left, top){
3252         delete this.lastXY;
3253         this.lastLT = [left, top];
3254     },
3255
3256     // private
3257     beforeFx : function(){
3258         this.beforeAction();
3259         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3260     },
3261
3262     // private
3263     afterFx : function(){
3264         Roo.Layer.superclass.afterFx.apply(this, arguments);
3265         this.sync(this.isVisible());
3266     },
3267
3268     // private
3269     beforeAction : function(){
3270         if(!this.updating && this.shadow){
3271             this.shadow.hide();
3272         }
3273     },
3274
3275     // overridden Element method
3276     setLeft : function(left){
3277         this.storeLeftTop(left, this.getTop(true));
3278         supr.setLeft.apply(this, arguments);
3279         this.sync();
3280     },
3281
3282     setTop : function(top){
3283         this.storeLeftTop(this.getLeft(true), top);
3284         supr.setTop.apply(this, arguments);
3285         this.sync();
3286     },
3287
3288     setLeftTop : function(left, top){
3289         this.storeLeftTop(left, top);
3290         supr.setLeftTop.apply(this, arguments);
3291         this.sync();
3292     },
3293
3294     setXY : function(xy, a, d, c, e){
3295         this.fixDisplay();
3296         this.beforeAction();
3297         this.storeXY(xy);
3298         var cb = this.createCB(c);
3299         supr.setXY.call(this, xy, a, d, cb, e);
3300         if(!a){
3301             cb();
3302         }
3303     },
3304
3305     // private
3306     createCB : function(c){
3307         var el = this;
3308         return function(){
3309             el.constrainXY();
3310             el.sync(true);
3311             if(c){
3312                 c();
3313             }
3314         };
3315     },
3316
3317     // overridden Element method
3318     setX : function(x, a, d, c, e){
3319         this.setXY([x, this.getY()], a, d, c, e);
3320     },
3321
3322     // overridden Element method
3323     setY : function(y, a, d, c, e){
3324         this.setXY([this.getX(), y], a, d, c, e);
3325     },
3326
3327     // overridden Element method
3328     setSize : function(w, h, a, d, c, e){
3329         this.beforeAction();
3330         var cb = this.createCB(c);
3331         supr.setSize.call(this, w, h, a, d, cb, e);
3332         if(!a){
3333             cb();
3334         }
3335     },
3336
3337     // overridden Element method
3338     setWidth : function(w, a, d, c, e){
3339         this.beforeAction();
3340         var cb = this.createCB(c);
3341         supr.setWidth.call(this, w, a, d, cb, e);
3342         if(!a){
3343             cb();
3344         }
3345     },
3346
3347     // overridden Element method
3348     setHeight : function(h, a, d, c, e){
3349         this.beforeAction();
3350         var cb = this.createCB(c);
3351         supr.setHeight.call(this, h, a, d, cb, e);
3352         if(!a){
3353             cb();
3354         }
3355     },
3356
3357     // overridden Element method
3358     setBounds : function(x, y, w, h, a, d, c, e){
3359         this.beforeAction();
3360         var cb = this.createCB(c);
3361         if(!a){
3362             this.storeXY([x, y]);
3363             supr.setXY.call(this, [x, y]);
3364             supr.setSize.call(this, w, h, a, d, cb, e);
3365             cb();
3366         }else{
3367             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3368         }
3369         return this;
3370     },
3371     
3372     /**
3373      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3374      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3375      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3376      * @param {Number} zindex The new z-index to set
3377      * @return {this} The Layer
3378      */
3379     setZIndex : function(zindex){
3380         this.zindex = zindex;
3381         this.setStyle("z-index", zindex + 2);
3382         if(this.shadow){
3383             this.shadow.setZIndex(zindex + 1);
3384         }
3385         if(this.shim){
3386             this.shim.setStyle("z-index", zindex);
3387         }
3388     }
3389 });
3390 })();/*
3391  * Based on:
3392  * Ext JS Library 1.1.1
3393  * Copyright(c) 2006-2007, Ext JS, LLC.
3394  *
3395  * Originally Released Under LGPL - original licence link has changed is not relivant.
3396  *
3397  * Fork - LGPL
3398  * <script type="text/javascript">
3399  */
3400
3401
3402 /**
3403  * @class Roo.Shadow
3404  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3405  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3406  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3407  * @constructor
3408  * Create a new Shadow
3409  * @param {Object} config The config object
3410  */
3411 Roo.Shadow = function(config){
3412     Roo.apply(this, config);
3413     if(typeof this.mode != "string"){
3414         this.mode = this.defaultMode;
3415     }
3416     var o = this.offset, a = {h: 0};
3417     var rad = Math.floor(this.offset/2);
3418     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3419         case "drop":
3420             a.w = 0;
3421             a.l = a.t = o;
3422             a.t -= 1;
3423             if(Roo.isIE){
3424                 a.l -= this.offset + rad;
3425                 a.t -= this.offset + rad;
3426                 a.w -= rad;
3427                 a.h -= rad;
3428                 a.t += 1;
3429             }
3430         break;
3431         case "sides":
3432             a.w = (o*2);
3433             a.l = -o;
3434             a.t = o-1;
3435             if(Roo.isIE){
3436                 a.l -= (this.offset - rad);
3437                 a.t -= this.offset + rad;
3438                 a.l += 1;
3439                 a.w -= (this.offset - rad)*2;
3440                 a.w -= rad + 1;
3441                 a.h -= 1;
3442             }
3443         break;
3444         case "frame":
3445             a.w = a.h = (o*2);
3446             a.l = a.t = -o;
3447             a.t += 1;
3448             a.h -= 2;
3449             if(Roo.isIE){
3450                 a.l -= (this.offset - rad);
3451                 a.t -= (this.offset - rad);
3452                 a.l += 1;
3453                 a.w -= (this.offset + rad + 1);
3454                 a.h -= (this.offset + rad);
3455                 a.h += 1;
3456             }
3457         break;
3458     };
3459
3460     this.adjusts = a;
3461 };
3462
3463 Roo.Shadow.prototype = {
3464     /**
3465      * @cfg {String} mode
3466      * The shadow display mode.  Supports the following options:<br />
3467      * sides: Shadow displays on both sides and bottom only<br />
3468      * frame: Shadow displays equally on all four sides<br />
3469      * drop: Traditional bottom-right drop shadow (default)
3470      */
3471     /**
3472      * @cfg {String} offset
3473      * The number of pixels to offset the shadow from the element (defaults to 4)
3474      */
3475     offset: 4,
3476
3477     // private
3478     defaultMode: "drop",
3479
3480     /**
3481      * Displays the shadow under the target element
3482      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3483      */
3484     show : function(target){
3485         target = Roo.get(target);
3486         if(!this.el){
3487             this.el = Roo.Shadow.Pool.pull();
3488             if(this.el.dom.nextSibling != target.dom){
3489                 this.el.insertBefore(target);
3490             }
3491         }
3492         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3493         if(Roo.isIE){
3494             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3495         }
3496         this.realign(
3497             target.getLeft(true),
3498             target.getTop(true),
3499             target.getWidth(),
3500             target.getHeight()
3501         );
3502         this.el.dom.style.display = "block";
3503     },
3504
3505     /**
3506      * Returns true if the shadow is visible, else false
3507      */
3508     isVisible : function(){
3509         return this.el ? true : false;  
3510     },
3511
3512     /**
3513      * Direct alignment when values are already available. Show must be called at least once before
3514      * calling this method to ensure it is initialized.
3515      * @param {Number} left The target element left position
3516      * @param {Number} top The target element top position
3517      * @param {Number} width The target element width
3518      * @param {Number} height The target element height
3519      */
3520     realign : function(l, t, w, h){
3521         if(!this.el){
3522             return;
3523         }
3524         var a = this.adjusts, d = this.el.dom, s = d.style;
3525         var iea = 0;
3526         s.left = (l+a.l)+"px";
3527         s.top = (t+a.t)+"px";
3528         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3529  
3530         if(s.width != sws || s.height != shs){
3531             s.width = sws;
3532             s.height = shs;
3533             if(!Roo.isIE){
3534                 var cn = d.childNodes;
3535                 var sww = Math.max(0, (sw-12))+"px";
3536                 cn[0].childNodes[1].style.width = sww;
3537                 cn[1].childNodes[1].style.width = sww;
3538                 cn[2].childNodes[1].style.width = sww;
3539                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3540             }
3541         }
3542     },
3543
3544     /**
3545      * Hides this shadow
3546      */
3547     hide : function(){
3548         if(this.el){
3549             this.el.dom.style.display = "none";
3550             Roo.Shadow.Pool.push(this.el);
3551             delete this.el;
3552         }
3553     },
3554
3555     /**
3556      * Adjust the z-index of this shadow
3557      * @param {Number} zindex The new z-index
3558      */
3559     setZIndex : function(z){
3560         this.zIndex = z;
3561         if(this.el){
3562             this.el.setStyle("z-index", z);
3563         }
3564     }
3565 };
3566
3567 // Private utility class that manages the internal Shadow cache
3568 Roo.Shadow.Pool = function(){
3569     var p = [];
3570     var markup = Roo.isIE ?
3571                  '<div class="x-ie-shadow"></div>' :
3572                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3573     return {
3574         pull : function(){
3575             var sh = p.shift();
3576             if(!sh){
3577                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3578                 sh.autoBoxAdjust = false;
3579             }
3580             return sh;
3581         },
3582
3583         push : function(sh){
3584             p.push(sh);
3585         }
3586     };
3587 }();/*
3588  * Based on:
3589  * Ext JS Library 1.1.1
3590  * Copyright(c) 2006-2007, Ext JS, LLC.
3591  *
3592  * Originally Released Under LGPL - original licence link has changed is not relivant.
3593  *
3594  * Fork - LGPL
3595  * <script type="text/javascript">
3596  */
3597
3598
3599 /**
3600  * @class Roo.SplitBar
3601  * @extends Roo.util.Observable
3602  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3603  * <br><br>
3604  * Usage:
3605  * <pre><code>
3606 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3607                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3608 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3609 split.minSize = 100;
3610 split.maxSize = 600;
3611 split.animate = true;
3612 split.on('moved', splitterMoved);
3613 </code></pre>
3614  * @constructor
3615  * Create a new SplitBar
3616  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3617  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3618  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3619  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3620                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3621                         position of the SplitBar).
3622  */
3623 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3624     
3625     /** @private */
3626     this.el = Roo.get(dragElement, true);
3627     this.el.dom.unselectable = "on";
3628     /** @private */
3629     this.resizingEl = Roo.get(resizingElement, true);
3630
3631     /**
3632      * @private
3633      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3634      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3635      * @type Number
3636      */
3637     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3638     
3639     /**
3640      * The minimum size of the resizing element. (Defaults to 0)
3641      * @type Number
3642      */
3643     this.minSize = 0;
3644     
3645     /**
3646      * The maximum size of the resizing element. (Defaults to 2000)
3647      * @type Number
3648      */
3649     this.maxSize = 2000;
3650     
3651     /**
3652      * Whether to animate the transition to the new size
3653      * @type Boolean
3654      */
3655     this.animate = false;
3656     
3657     /**
3658      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3659      * @type Boolean
3660      */
3661     this.useShim = false;
3662     
3663     /** @private */
3664     this.shim = null;
3665     
3666     if(!existingProxy){
3667         /** @private */
3668         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3669     }else{
3670         this.proxy = Roo.get(existingProxy).dom;
3671     }
3672     /** @private */
3673     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3674     
3675     /** @private */
3676     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3677     
3678     /** @private */
3679     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3680     
3681     /** @private */
3682     this.dragSpecs = {};
3683     
3684     /**
3685      * @private The adapter to use to positon and resize elements
3686      */
3687     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3688     this.adapter.init(this);
3689     
3690     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3691         /** @private */
3692         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3693         this.el.addClass("x-splitbar-h");
3694     }else{
3695         /** @private */
3696         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3697         this.el.addClass("x-splitbar-v");
3698     }
3699     
3700     this.addEvents({
3701         /**
3702          * @event resize
3703          * Fires when the splitter is moved (alias for {@link #event-moved})
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "resize" : true,
3708         /**
3709          * @event moved
3710          * Fires when the splitter is moved
3711          * @param {Roo.SplitBar} this
3712          * @param {Number} newSize the new width or height
3713          */
3714         "moved" : true,
3715         /**
3716          * @event beforeresize
3717          * Fires before the splitter is dragged
3718          * @param {Roo.SplitBar} this
3719          */
3720         "beforeresize" : true,
3721
3722         "beforeapply" : true
3723     });
3724
3725     Roo.util.Observable.call(this);
3726 };
3727
3728 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3729     onStartProxyDrag : function(x, y){
3730         this.fireEvent("beforeresize", this);
3731         if(!this.overlay){
3732             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3733             o.unselectable();
3734             o.enableDisplayMode("block");
3735             // all splitbars share the same overlay
3736             Roo.SplitBar.prototype.overlay = o;
3737         }
3738         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3739         this.overlay.show();
3740         Roo.get(this.proxy).setDisplayed("block");
3741         var size = this.adapter.getElementSize(this);
3742         this.activeMinSize = this.getMinimumSize();;
3743         this.activeMaxSize = this.getMaximumSize();;
3744         var c1 = size - this.activeMinSize;
3745         var c2 = Math.max(this.activeMaxSize - size, 0);
3746         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(
3749                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3751             );
3752             this.dd.setYConstraint(0, 0);
3753         }else{
3754             this.dd.resetConstraints();
3755             this.dd.setXConstraint(0, 0);
3756             this.dd.setYConstraint(
3757                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3758                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3759             );
3760          }
3761         this.dragSpecs.startSize = size;
3762         this.dragSpecs.startPoint = [x, y];
3763         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3764     },
3765     
3766     /** 
3767      * @private Called after the drag operation by the DDProxy
3768      */
3769     onEndProxyDrag : function(e){
3770         Roo.get(this.proxy).setDisplayed(false);
3771         var endPoint = Roo.lib.Event.getXY(e);
3772         if(this.overlay){
3773             this.overlay.hide();
3774         }
3775         var newSize;
3776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3777             newSize = this.dragSpecs.startSize + 
3778                 (this.placement == Roo.SplitBar.LEFT ?
3779                     endPoint[0] - this.dragSpecs.startPoint[0] :
3780                     this.dragSpecs.startPoint[0] - endPoint[0]
3781                 );
3782         }else{
3783             newSize = this.dragSpecs.startSize + 
3784                 (this.placement == Roo.SplitBar.TOP ?
3785                     endPoint[1] - this.dragSpecs.startPoint[1] :
3786                     this.dragSpecs.startPoint[1] - endPoint[1]
3787                 );
3788         }
3789         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3790         if(newSize != this.dragSpecs.startSize){
3791             if(this.fireEvent('beforeapply', this, newSize) !== false){
3792                 this.adapter.setElementSize(this, newSize);
3793                 this.fireEvent("moved", this, newSize);
3794                 this.fireEvent("resize", this, newSize);
3795             }
3796         }
3797     },
3798     
3799     /**
3800      * Get the adapter this SplitBar uses
3801      * @return The adapter object
3802      */
3803     getAdapter : function(){
3804         return this.adapter;
3805     },
3806     
3807     /**
3808      * Set the adapter this SplitBar uses
3809      * @param {Object} adapter A SplitBar adapter object
3810      */
3811     setAdapter : function(adapter){
3812         this.adapter = adapter;
3813         this.adapter.init(this);
3814     },
3815     
3816     /**
3817      * Gets the minimum size for the resizing element
3818      * @return {Number} The minimum size
3819      */
3820     getMinimumSize : function(){
3821         return this.minSize;
3822     },
3823     
3824     /**
3825      * Sets the minimum size for the resizing element
3826      * @param {Number} minSize The minimum size
3827      */
3828     setMinimumSize : function(minSize){
3829         this.minSize = minSize;
3830     },
3831     
3832     /**
3833      * Gets the maximum size for the resizing element
3834      * @return {Number} The maximum size
3835      */
3836     getMaximumSize : function(){
3837         return this.maxSize;
3838     },
3839     
3840     /**
3841      * Sets the maximum size for the resizing element
3842      * @param {Number} maxSize The maximum size
3843      */
3844     setMaximumSize : function(maxSize){
3845         this.maxSize = maxSize;
3846     },
3847     
3848     /**
3849      * Sets the initialize size for the resizing element
3850      * @param {Number} size The initial size
3851      */
3852     setCurrentSize : function(size){
3853         var oldAnimate = this.animate;
3854         this.animate = false;
3855         this.adapter.setElementSize(this, size);
3856         this.animate = oldAnimate;
3857     },
3858     
3859     /**
3860      * Destroy this splitbar. 
3861      * @param {Boolean} removeEl True to remove the element
3862      */
3863     destroy : function(removeEl){
3864         if(this.shim){
3865             this.shim.remove();
3866         }
3867         this.dd.unreg();
3868         this.proxy.parentNode.removeChild(this.proxy);
3869         if(removeEl){
3870             this.el.remove();
3871         }
3872     }
3873 });
3874
3875 /**
3876  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3877  */
3878 Roo.SplitBar.createProxy = function(dir){
3879     var proxy = new Roo.Element(document.createElement("div"));
3880     proxy.unselectable();
3881     var cls = 'x-splitbar-proxy';
3882     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3883     document.body.appendChild(proxy.dom);
3884     return proxy.dom;
3885 };
3886
3887 /** 
3888  * @class Roo.SplitBar.BasicLayoutAdapter
3889  * Default Adapter. It assumes the splitter and resizing element are not positioned
3890  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3891  */
3892 Roo.SplitBar.BasicLayoutAdapter = function(){
3893 };
3894
3895 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3896     // do nothing for now
3897     init : function(s){
3898     
3899     },
3900     /**
3901      * Called before drag operations to get the current size of the resizing element. 
3902      * @param {Roo.SplitBar} s The SplitBar using this adapter
3903      */
3904      getElementSize : function(s){
3905         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3906             return s.resizingEl.getWidth();
3907         }else{
3908             return s.resizingEl.getHeight();
3909         }
3910     },
3911     
3912     /**
3913      * Called after drag operations to set the size of the resizing element.
3914      * @param {Roo.SplitBar} s The SplitBar using this adapter
3915      * @param {Number} newSize The new size to set
3916      * @param {Function} onComplete A function to be invoked when resizing is complete
3917      */
3918     setElementSize : function(s, newSize, onComplete){
3919         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3920             if(!s.animate){
3921                 s.resizingEl.setWidth(newSize);
3922                 if(onComplete){
3923                     onComplete(s, newSize);
3924                 }
3925             }else{
3926                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3927             }
3928         }else{
3929             
3930             if(!s.animate){
3931                 s.resizingEl.setHeight(newSize);
3932                 if(onComplete){
3933                     onComplete(s, newSize);
3934                 }
3935             }else{
3936                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3937             }
3938         }
3939     }
3940 };
3941
3942 /** 
3943  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3944  * @extends Roo.SplitBar.BasicLayoutAdapter
3945  * Adapter that  moves the splitter element to align with the resized sizing element. 
3946  * Used with an absolute positioned SplitBar.
3947  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3948  * document.body, make sure you assign an id to the body element.
3949  */
3950 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3951     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3952     this.container = Roo.get(container);
3953 };
3954
3955 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3956     init : function(s){
3957         this.basic.init(s);
3958     },
3959     
3960     getElementSize : function(s){
3961         return this.basic.getElementSize(s);
3962     },
3963     
3964     setElementSize : function(s, newSize, onComplete){
3965         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3966     },
3967     
3968     moveSplitter : function(s){
3969         var yes = Roo.SplitBar;
3970         switch(s.placement){
3971             case yes.LEFT:
3972                 s.el.setX(s.resizingEl.getRight());
3973                 break;
3974             case yes.RIGHT:
3975                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3976                 break;
3977             case yes.TOP:
3978                 s.el.setY(s.resizingEl.getBottom());
3979                 break;
3980             case yes.BOTTOM:
3981                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3982                 break;
3983         }
3984     }
3985 };
3986
3987 /**
3988  * Orientation constant - Create a vertical SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.VERTICAL = 1;
3993
3994 /**
3995  * Orientation constant - Create a horizontal SplitBar
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.HORIZONTAL = 2;
4000
4001 /**
4002  * Placement constant - The resizing element is to the left of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.LEFT = 1;
4007
4008 /**
4009  * Placement constant - The resizing element is to the right of the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.RIGHT = 2;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned above the splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.TOP = 3;
4021
4022 /**
4023  * Placement constant - The resizing element is positioned under splitter element
4024  * @static
4025  * @type Number
4026  */
4027 Roo.SplitBar.BOTTOM = 4;
4028 /*
4029  * Based on:
4030  * Ext JS Library 1.1.1
4031  * Copyright(c) 2006-2007, Ext JS, LLC.
4032  *
4033  * Originally Released Under LGPL - original licence link has changed is not relivant.
4034  *
4035  * Fork - LGPL
4036  * <script type="text/javascript">
4037  */
4038
4039 /**
4040  * @class Roo.View
4041  * @extends Roo.util.Observable
4042  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4043  * This class also supports single and multi selection modes. <br>
4044  * Create a data model bound view:
4045  <pre><code>
4046  var store = new Roo.data.Store(...);
4047
4048  var view = new Roo.View({
4049     el : "my-element",
4050     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4051  
4052     singleSelect: true,
4053     selectedClass: "ydataview-selected",
4054     store: store
4055  });
4056
4057  // listen for node click?
4058  view.on("click", function(vw, index, node, e){
4059  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4060  });
4061
4062  // load XML data
4063  dataModel.load("foobar.xml");
4064  </code></pre>
4065  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4066  * <br><br>
4067  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4068  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4069  * 
4070  * Note: old style constructor is still suported (container, template, config)
4071  * 
4072  * @constructor
4073  * Create a new View
4074  * @param {Object} config The config object
4075  * 
4076  */
4077 Roo.View = function(config, depreciated_tpl, depreciated_config){
4078     
4079     this.parent = false;
4080     
4081     if (typeof(depreciated_tpl) == 'undefined') {
4082         // new way.. - universal constructor.
4083         Roo.apply(this, config);
4084         this.el  = Roo.get(this.el);
4085     } else {
4086         // old format..
4087         this.el  = Roo.get(config);
4088         this.tpl = depreciated_tpl;
4089         Roo.apply(this, depreciated_config);
4090     }
4091     this.wrapEl  = this.el.wrap().wrap();
4092     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4093     
4094     
4095     if(typeof(this.tpl) == "string"){
4096         this.tpl = new Roo.Template(this.tpl);
4097     } else {
4098         // support xtype ctors..
4099         this.tpl = new Roo.factory(this.tpl, Roo);
4100     }
4101     
4102     
4103     this.tpl.compile();
4104     
4105     /** @private */
4106     this.addEvents({
4107         /**
4108          * @event beforeclick
4109          * Fires before a click is processed. Returns false to cancel the default action.
4110          * @param {Roo.View} this
4111          * @param {Number} index The index of the target node
4112          * @param {HTMLElement} node The target node
4113          * @param {Roo.EventObject} e The raw event object
4114          */
4115             "beforeclick" : true,
4116         /**
4117          * @event click
4118          * Fires when a template node is clicked.
4119          * @param {Roo.View} this
4120          * @param {Number} index The index of the target node
4121          * @param {HTMLElement} node The target node
4122          * @param {Roo.EventObject} e The raw event object
4123          */
4124             "click" : true,
4125         /**
4126          * @event dblclick
4127          * Fires when a template node is double clicked.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "dblclick" : true,
4134         /**
4135          * @event contextmenu
4136          * Fires when a template node is right clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "contextmenu" : true,
4143         /**
4144          * @event selectionchange
4145          * Fires when the selected nodes change.
4146          * @param {Roo.View} this
4147          * @param {Array} selections Array of the selected nodes
4148          */
4149             "selectionchange" : true,
4150     
4151         /**
4152          * @event beforeselect
4153          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4154          * @param {Roo.View} this
4155          * @param {HTMLElement} node The node to be selected
4156          * @param {Array} selections Array of currently selected nodes
4157          */
4158             "beforeselect" : true,
4159         /**
4160          * @event preparedata
4161          * Fires on every row to render, to allow you to change the data.
4162          * @param {Roo.View} this
4163          * @param {Object} data to be rendered (change this)
4164          */
4165           "preparedata" : true
4166           
4167           
4168         });
4169
4170
4171
4172     this.el.on({
4173         "click": this.onClick,
4174         "dblclick": this.onDblClick,
4175         "contextmenu": this.onContextMenu,
4176         scope:this
4177     });
4178
4179     this.selections = [];
4180     this.nodes = [];
4181     this.cmp = new Roo.CompositeElementLite([]);
4182     if(this.store){
4183         this.store = Roo.factory(this.store, Roo.data);
4184         this.setStore(this.store, true);
4185     }
4186     
4187     if ( this.footer && this.footer.xtype) {
4188            
4189          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4190         
4191         this.footer.dataSource = this.store;
4192         this.footer.container = fctr;
4193         this.footer = Roo.factory(this.footer, Roo);
4194         fctr.insertFirst(this.el);
4195         
4196         // this is a bit insane - as the paging toolbar seems to detach the el..
4197 //        dom.parentNode.parentNode.parentNode
4198          // they get detached?
4199     }
4200     
4201     
4202     Roo.View.superclass.constructor.call(this);
4203     
4204     
4205 };
4206
4207 Roo.extend(Roo.View, Roo.util.Observable, {
4208     
4209      /**
4210      * @cfg {Roo.data.Store} store Data store to load data from.
4211      */
4212     store : false,
4213     
4214     /**
4215      * @cfg {String|Roo.Element} el The container element.
4216      */
4217     el : '',
4218     
4219     /**
4220      * @cfg {String|Roo.Template} tpl The template used by this View 
4221      */
4222     tpl : false,
4223     /**
4224      * @cfg {String} dataName the named area of the template to use as the data area
4225      *                          Works with domtemplates roo-name="name"
4226      */
4227     dataName: false,
4228     /**
4229      * @cfg {String} selectedClass The css class to add to selected nodes
4230      */
4231     selectedClass : "x-view-selected",
4232      /**
4233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4234      */
4235     emptyText : "",
4236     
4237     /**
4238      * @cfg {String} text to display on mask (default Loading)
4239      */
4240     mask : false,
4241     /**
4242      * @cfg {Boolean} multiSelect Allow multiple selection
4243      */
4244     multiSelect : false,
4245     /**
4246      * @cfg {Boolean} singleSelect Allow single selection
4247      */
4248     singleSelect:  false,
4249     
4250     /**
4251      * @cfg {Boolean} toggleSelect - selecting 
4252      */
4253     toggleSelect : false,
4254     
4255     /**
4256      * @cfg {Boolean} tickable - selecting 
4257      */
4258     tickable : false,
4259     
4260     /**
4261      * Returns the element this view is bound to.
4262      * @return {Roo.Element}
4263      */
4264     getEl : function(){
4265         return this.wrapEl;
4266     },
4267     
4268     
4269
4270     /**
4271      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4272      */
4273     refresh : function(){
4274         //Roo.log('refresh');
4275         var t = this.tpl;
4276         
4277         // if we are using something like 'domtemplate', then
4278         // the what gets used is:
4279         // t.applySubtemplate(NAME, data, wrapping data..)
4280         // the outer template then get' applied with
4281         //     the store 'extra data'
4282         // and the body get's added to the
4283         //      roo-name="data" node?
4284         //      <span class='roo-tpl-{name}'></span> ?????
4285         
4286         
4287         
4288         this.clearSelections();
4289         this.el.update("");
4290         var html = [];
4291         var records = this.store.getRange();
4292         if(records.length < 1) {
4293             
4294             // is this valid??  = should it render a template??
4295             
4296             this.el.update(this.emptyText);
4297             return;
4298         }
4299         var el = this.el;
4300         if (this.dataName) {
4301             this.el.update(t.apply(this.store.meta)); //????
4302             el = this.el.child('.roo-tpl-' + this.dataName);
4303         }
4304         
4305         for(var i = 0, len = records.length; i < len; i++){
4306             var data = this.prepareData(records[i].data, i, records[i]);
4307             this.fireEvent("preparedata", this, data, i, records[i]);
4308             
4309             var d = Roo.apply({}, data);
4310             
4311             if(this.tickable){
4312                 Roo.apply(d, {'roo-id' : Roo.id()});
4313                 
4314                 var _this = this;
4315             
4316                 Roo.each(this.parent.item, function(item){
4317                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4318                         return;
4319                     }
4320                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4321                 });
4322             }
4323             
4324             html[html.length] = Roo.util.Format.trim(
4325                 this.dataName ?
4326                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4327                     t.apply(d)
4328             );
4329         }
4330         
4331         
4332         
4333         el.update(html.join(""));
4334         this.nodes = el.dom.childNodes;
4335         this.updateIndexes(0);
4336     },
4337     
4338
4339     /**
4340      * Function to override to reformat the data that is sent to
4341      * the template for each node.
4342      * DEPRICATED - use the preparedata event handler.
4343      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4344      * a JSON object for an UpdateManager bound view).
4345      */
4346     prepareData : function(data, index, record)
4347     {
4348         this.fireEvent("preparedata", this, data, index, record);
4349         return data;
4350     },
4351
4352     onUpdate : function(ds, record){
4353         // Roo.log('on update');   
4354         this.clearSelections();
4355         var index = this.store.indexOf(record);
4356         var n = this.nodes[index];
4357         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4358         n.parentNode.removeChild(n);
4359         this.updateIndexes(index, index);
4360     },
4361
4362     
4363     
4364 // --------- FIXME     
4365     onAdd : function(ds, records, index)
4366     {
4367         //Roo.log(['on Add', ds, records, index] );        
4368         this.clearSelections();
4369         if(this.nodes.length == 0){
4370             this.refresh();
4371             return;
4372         }
4373         var n = this.nodes[index];
4374         for(var i = 0, len = records.length; i < len; i++){
4375             var d = this.prepareData(records[i].data, i, records[i]);
4376             if(n){
4377                 this.tpl.insertBefore(n, d);
4378             }else{
4379                 
4380                 this.tpl.append(this.el, d);
4381             }
4382         }
4383         this.updateIndexes(index);
4384     },
4385
4386     onRemove : function(ds, record, index){
4387        // Roo.log('onRemove');
4388         this.clearSelections();
4389         var el = this.dataName  ?
4390             this.el.child('.roo-tpl-' + this.dataName) :
4391             this.el; 
4392         
4393         el.dom.removeChild(this.nodes[index]);
4394         this.updateIndexes(index);
4395     },
4396
4397     /**
4398      * Refresh an individual node.
4399      * @param {Number} index
4400      */
4401     refreshNode : function(index){
4402         this.onUpdate(this.store, this.store.getAt(index));
4403     },
4404
4405     updateIndexes : function(startIndex, endIndex){
4406         var ns = this.nodes;
4407         startIndex = startIndex || 0;
4408         endIndex = endIndex || ns.length - 1;
4409         for(var i = startIndex; i <= endIndex; i++){
4410             ns[i].nodeIndex = i;
4411         }
4412     },
4413
4414     /**
4415      * Changes the data store this view uses and refresh the view.
4416      * @param {Store} store
4417      */
4418     setStore : function(store, initial){
4419         if(!initial && this.store){
4420             this.store.un("datachanged", this.refresh);
4421             this.store.un("add", this.onAdd);
4422             this.store.un("remove", this.onRemove);
4423             this.store.un("update", this.onUpdate);
4424             this.store.un("clear", this.refresh);
4425             this.store.un("beforeload", this.onBeforeLoad);
4426             this.store.un("load", this.onLoad);
4427             this.store.un("loadexception", this.onLoad);
4428         }
4429         if(store){
4430           
4431             store.on("datachanged", this.refresh, this);
4432             store.on("add", this.onAdd, this);
4433             store.on("remove", this.onRemove, this);
4434             store.on("update", this.onUpdate, this);
4435             store.on("clear", this.refresh, this);
4436             store.on("beforeload", this.onBeforeLoad, this);
4437             store.on("load", this.onLoad, this);
4438             store.on("loadexception", this.onLoad, this);
4439         }
4440         
4441         if(store){
4442             this.refresh();
4443         }
4444     },
4445     /**
4446      * onbeforeLoad - masks the loading area.
4447      *
4448      */
4449     onBeforeLoad : function(store,opts)
4450     {
4451          //Roo.log('onBeforeLoad');   
4452         if (!opts.add) {
4453             this.el.update("");
4454         }
4455         this.el.mask(this.mask ? this.mask : "Loading" ); 
4456     },
4457     onLoad : function ()
4458     {
4459         this.el.unmask();
4460     },
4461     
4462
4463     /**
4464      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4465      * @param {HTMLElement} node
4466      * @return {HTMLElement} The template node
4467      */
4468     findItemFromChild : function(node){
4469         var el = this.dataName  ?
4470             this.el.child('.roo-tpl-' + this.dataName,true) :
4471             this.el.dom; 
4472         
4473         if(!node || node.parentNode == el){
4474                     return node;
4475             }
4476             var p = node.parentNode;
4477             while(p && p != el){
4478             if(p.parentNode == el){
4479                 return p;
4480             }
4481             p = p.parentNode;
4482         }
4483             return null;
4484     },
4485
4486     /** @ignore */
4487     onClick : function(e){
4488         var item = this.findItemFromChild(e.getTarget());
4489         if(item){
4490             var index = this.indexOf(item);
4491             if(this.onItemClick(item, index, e) !== false){
4492                 this.fireEvent("click", this, index, item, e);
4493             }
4494         }else{
4495             this.clearSelections();
4496         }
4497     },
4498
4499     /** @ignore */
4500     onContextMenu : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     /** @ignore */
4508     onDblClick : function(e){
4509         var item = this.findItemFromChild(e.getTarget());
4510         if(item){
4511             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4512         }
4513     },
4514
4515     onItemClick : function(item, index, e)
4516     {
4517         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4518             return false;
4519         }
4520         if (this.toggleSelect) {
4521             var m = this.isSelected(item) ? 'unselect' : 'select';
4522             //Roo.log(m);
4523             var _t = this;
4524             _t[m](item, true, false);
4525             return true;
4526         }
4527         if(this.multiSelect || this.singleSelect){
4528             if(this.multiSelect && e.shiftKey && this.lastSelection){
4529                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4530             }else{
4531                 this.select(item, this.multiSelect && e.ctrlKey);
4532                 this.lastSelection = item;
4533             }
4534             
4535             if(!this.tickable){
4536                 e.preventDefault();
4537             }
4538             
4539         }
4540         return true;
4541     },
4542
4543     /**
4544      * Get the number of selected nodes.
4545      * @return {Number}
4546      */
4547     getSelectionCount : function(){
4548         return this.selections.length;
4549     },
4550
4551     /**
4552      * Get the currently selected nodes.
4553      * @return {Array} An array of HTMLElements
4554      */
4555     getSelectedNodes : function(){
4556         return this.selections;
4557     },
4558
4559     /**
4560      * Get the indexes of the selected nodes.
4561      * @return {Array}
4562      */
4563     getSelectedIndexes : function(){
4564         var indexes = [], s = this.selections;
4565         for(var i = 0, len = s.length; i < len; i++){
4566             indexes.push(s[i].nodeIndex);
4567         }
4568         return indexes;
4569     },
4570
4571     /**
4572      * Clear all selections
4573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4574      */
4575     clearSelections : function(suppressEvent){
4576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4577             this.cmp.elements = this.selections;
4578             this.cmp.removeClass(this.selectedClass);
4579             this.selections = [];
4580             if(!suppressEvent){
4581                 this.fireEvent("selectionchange", this, this.selections);
4582             }
4583         }
4584     },
4585
4586     /**
4587      * Returns true if the passed node is selected
4588      * @param {HTMLElement/Number} node The node or node index
4589      * @return {Boolean}
4590      */
4591     isSelected : function(node){
4592         var s = this.selections;
4593         if(s.length < 1){
4594             return false;
4595         }
4596         node = this.getNode(node);
4597         return s.indexOf(node) !== -1;
4598     },
4599
4600     /**
4601      * Selects nodes.
4602      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4603      * @param {Boolean} keepExisting (optional) true to keep existing selections
4604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4605      */
4606     select : function(nodeInfo, keepExisting, suppressEvent){
4607         if(nodeInfo instanceof Array){
4608             if(!keepExisting){
4609                 this.clearSelections(true);
4610             }
4611             for(var i = 0, len = nodeInfo.length; i < len; i++){
4612                 this.select(nodeInfo[i], true, true);
4613             }
4614             return;
4615         } 
4616         var node = this.getNode(nodeInfo);
4617         if(!node || this.isSelected(node)){
4618             return; // already selected.
4619         }
4620         if(!keepExisting){
4621             this.clearSelections(true);
4622         }
4623         
4624         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4625             Roo.fly(node).addClass(this.selectedClass);
4626             this.selections.push(node);
4627             if(!suppressEvent){
4628                 this.fireEvent("selectionchange", this, this.selections);
4629             }
4630         }
4631         
4632         
4633     },
4634       /**
4635      * Unselects nodes.
4636      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4637      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4639      */
4640     unselect : function(nodeInfo, keepExisting, suppressEvent)
4641     {
4642         if(nodeInfo instanceof Array){
4643             Roo.each(this.selections, function(s) {
4644                 this.unselect(s, nodeInfo);
4645             }, this);
4646             return;
4647         }
4648         var node = this.getNode(nodeInfo);
4649         if(!node || !this.isSelected(node)){
4650             //Roo.log("not selected");
4651             return; // not selected.
4652         }
4653         // fireevent???
4654         var ns = [];
4655         Roo.each(this.selections, function(s) {
4656             if (s == node ) {
4657                 Roo.fly(node).removeClass(this.selectedClass);
4658
4659                 return;
4660             }
4661             ns.push(s);
4662         },this);
4663         
4664         this.selections= ns;
4665         this.fireEvent("selectionchange", this, this.selections);
4666     },
4667
4668     /**
4669      * Gets a template node.
4670      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4671      * @return {HTMLElement} The node or null if it wasn't found
4672      */
4673     getNode : function(nodeInfo){
4674         if(typeof nodeInfo == "string"){
4675             return document.getElementById(nodeInfo);
4676         }else if(typeof nodeInfo == "number"){
4677             return this.nodes[nodeInfo];
4678         }
4679         return nodeInfo;
4680     },
4681
4682     /**
4683      * Gets a range template nodes.
4684      * @param {Number} startIndex
4685      * @param {Number} endIndex
4686      * @return {Array} An array of nodes
4687      */
4688     getNodes : function(start, end){
4689         var ns = this.nodes;
4690         start = start || 0;
4691         end = typeof end == "undefined" ? ns.length - 1 : end;
4692         var nodes = [];
4693         if(start <= end){
4694             for(var i = start; i <= end; i++){
4695                 nodes.push(ns[i]);
4696             }
4697         } else{
4698             for(var i = start; i >= end; i--){
4699                 nodes.push(ns[i]);
4700             }
4701         }
4702         return nodes;
4703     },
4704
4705     /**
4706      * Finds the index of the passed node
4707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4708      * @return {Number} The index of the node or -1
4709      */
4710     indexOf : function(node){
4711         node = this.getNode(node);
4712         if(typeof node.nodeIndex == "number"){
4713             return node.nodeIndex;
4714         }
4715         var ns = this.nodes;
4716         for(var i = 0, len = ns.length; i < len; i++){
4717             if(ns[i] == node){
4718                 return i;
4719             }
4720         }
4721         return -1;
4722     }
4723 });
4724 /*
4725  * Based on:
4726  * Ext JS Library 1.1.1
4727  * Copyright(c) 2006-2007, Ext JS, LLC.
4728  *
4729  * Originally Released Under LGPL - original licence link has changed is not relivant.
4730  *
4731  * Fork - LGPL
4732  * <script type="text/javascript">
4733  */
4734
4735 /**
4736  * @class Roo.JsonView
4737  * @extends Roo.View
4738  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4739 <pre><code>
4740 var view = new Roo.JsonView({
4741     container: "my-element",
4742     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4743     multiSelect: true, 
4744     jsonRoot: "data" 
4745 });
4746
4747 // listen for node click?
4748 view.on("click", function(vw, index, node, e){
4749     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4750 });
4751
4752 // direct load of JSON data
4753 view.load("foobar.php");
4754
4755 // Example from my blog list
4756 var tpl = new Roo.Template(
4757     '&lt;div class="entry"&gt;' +
4758     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4759     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4760     "&lt;/div&gt;&lt;hr /&gt;"
4761 );
4762
4763 var moreView = new Roo.JsonView({
4764     container :  "entry-list", 
4765     template : tpl,
4766     jsonRoot: "posts"
4767 });
4768 moreView.on("beforerender", this.sortEntries, this);
4769 moreView.load({
4770     url: "/blog/get-posts.php",
4771     params: "allposts=true",
4772     text: "Loading Blog Entries..."
4773 });
4774 </code></pre>
4775
4776 * Note: old code is supported with arguments : (container, template, config)
4777
4778
4779  * @constructor
4780  * Create a new JsonView
4781  * 
4782  * @param {Object} config The config object
4783  * 
4784  */
4785 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4786     
4787     
4788     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4789
4790     var um = this.el.getUpdateManager();
4791     um.setRenderer(this);
4792     um.on("update", this.onLoad, this);
4793     um.on("failure", this.onLoadException, this);
4794
4795     /**
4796      * @event beforerender
4797      * Fires before rendering of the downloaded JSON data.
4798      * @param {Roo.JsonView} this
4799      * @param {Object} data The JSON data loaded
4800      */
4801     /**
4802      * @event load
4803      * Fires when data is loaded.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} data The JSON data loaded
4806      * @param {Object} response The raw Connect response object
4807      */
4808     /**
4809      * @event loadexception
4810      * Fires when loading fails.
4811      * @param {Roo.JsonView} this
4812      * @param {Object} response The raw Connect response object
4813      */
4814     this.addEvents({
4815         'beforerender' : true,
4816         'load' : true,
4817         'loadexception' : true
4818     });
4819 };
4820 Roo.extend(Roo.JsonView, Roo.View, {
4821     /**
4822      * @type {String} The root property in the loaded JSON object that contains the data
4823      */
4824     jsonRoot : "",
4825
4826     /**
4827      * Refreshes the view.
4828      */
4829     refresh : function(){
4830         this.clearSelections();
4831         this.el.update("");
4832         var html = [];
4833         var o = this.jsonData;
4834         if(o && o.length > 0){
4835             for(var i = 0, len = o.length; i < len; i++){
4836                 var data = this.prepareData(o[i], i, o);
4837                 html[html.length] = this.tpl.apply(data);
4838             }
4839         }else{
4840             html.push(this.emptyText);
4841         }
4842         this.el.update(html.join(""));
4843         this.nodes = this.el.dom.childNodes;
4844         this.updateIndexes(0);
4845     },
4846
4847     /**
4848      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4849      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4850      <pre><code>
4851      view.load({
4852          url: "your-url.php",
4853          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4854          callback: yourFunction,
4855          scope: yourObject, //(optional scope)
4856          discardUrl: false,
4857          nocache: false,
4858          text: "Loading...",
4859          timeout: 30,
4860          scripts: false
4861      });
4862      </code></pre>
4863      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4864      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4865      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4867      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4868      */
4869     load : function(){
4870         var um = this.el.getUpdateManager();
4871         um.update.apply(um, arguments);
4872     },
4873
4874     // note - render is a standard framework call...
4875     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4876     render : function(el, response){
4877         
4878         this.clearSelections();
4879         this.el.update("");
4880         var o;
4881         try{
4882             if (response != '') {
4883                 o = Roo.util.JSON.decode(response.responseText);
4884                 if(this.jsonRoot){
4885                     
4886                     o = o[this.jsonRoot];
4887                 }
4888             }
4889         } catch(e){
4890         }
4891         /**
4892          * The current JSON data or null
4893          */
4894         this.jsonData = o;
4895         this.beforeRender();
4896         this.refresh();
4897     },
4898
4899 /**
4900  * Get the number of records in the current JSON dataset
4901  * @return {Number}
4902  */
4903     getCount : function(){
4904         return this.jsonData ? this.jsonData.length : 0;
4905     },
4906
4907 /**
4908  * Returns the JSON object for the specified node(s)
4909  * @param {HTMLElement/Array} node The node or an array of nodes
4910  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4911  * you get the JSON object for the node
4912  */
4913     getNodeData : function(node){
4914         if(node instanceof Array){
4915             var data = [];
4916             for(var i = 0, len = node.length; i < len; i++){
4917                 data.push(this.getNodeData(node[i]));
4918             }
4919             return data;
4920         }
4921         return this.jsonData[this.indexOf(node)] || null;
4922     },
4923
4924     beforeRender : function(){
4925         this.snapshot = this.jsonData;
4926         if(this.sortInfo){
4927             this.sort.apply(this, this.sortInfo);
4928         }
4929         this.fireEvent("beforerender", this, this.jsonData);
4930     },
4931
4932     onLoad : function(el, o){
4933         this.fireEvent("load", this, this.jsonData, o);
4934     },
4935
4936     onLoadException : function(el, o){
4937         this.fireEvent("loadexception", this, o);
4938     },
4939
4940 /**
4941  * Filter the data by a specific property.
4942  * @param {String} property A property on your JSON objects
4943  * @param {String/RegExp} value Either string that the property values
4944  * should start with, or a RegExp to test against the property
4945  */
4946     filter : function(property, value){
4947         if(this.jsonData){
4948             var data = [];
4949             var ss = this.snapshot;
4950             if(typeof value == "string"){
4951                 var vlen = value.length;
4952                 if(vlen == 0){
4953                     this.clearFilter();
4954                     return;
4955                 }
4956                 value = value.toLowerCase();
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(o[property].substr(0, vlen).toLowerCase() == value){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else if(value.exec){ // regex?
4964                 for(var i = 0, len = ss.length; i < len; i++){
4965                     var o = ss[i];
4966                     if(value.test(o[property])){
4967                         data.push(o);
4968                     }
4969                 }
4970             } else{
4971                 return;
4972             }
4973             this.jsonData = data;
4974             this.refresh();
4975         }
4976     },
4977
4978 /**
4979  * Filter by a function. The passed function will be called with each
4980  * object in the current dataset. If the function returns true the value is kept,
4981  * otherwise it is filtered.
4982  * @param {Function} fn
4983  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4984  */
4985     filterBy : function(fn, scope){
4986         if(this.jsonData){
4987             var data = [];
4988             var ss = this.snapshot;
4989             for(var i = 0, len = ss.length; i < len; i++){
4990                 var o = ss[i];
4991                 if(fn.call(scope || this, o)){
4992                     data.push(o);
4993                 }
4994             }
4995             this.jsonData = data;
4996             this.refresh();
4997         }
4998     },
4999
5000 /**
5001  * Clears the current filter.
5002  */
5003     clearFilter : function(){
5004         if(this.snapshot && this.jsonData != this.snapshot){
5005             this.jsonData = this.snapshot;
5006             this.refresh();
5007         }
5008     },
5009
5010
5011 /**
5012  * Sorts the data for this view and refreshes it.
5013  * @param {String} property A property on your JSON objects to sort on
5014  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5015  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5016  */
5017     sort : function(property, dir, sortType){
5018         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5019         if(this.jsonData){
5020             var p = property;
5021             var dsc = dir && dir.toLowerCase() == "desc";
5022             var f = function(o1, o2){
5023                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5024                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5025                 ;
5026                 if(v1 < v2){
5027                     return dsc ? +1 : -1;
5028                 } else if(v1 > v2){
5029                     return dsc ? -1 : +1;
5030                 } else{
5031                     return 0;
5032                 }
5033             };
5034             this.jsonData.sort(f);
5035             this.refresh();
5036             if(this.jsonData != this.snapshot){
5037                 this.snapshot.sort(f);
5038             }
5039         }
5040     }
5041 });/*
5042  * Based on:
5043  * Ext JS Library 1.1.1
5044  * Copyright(c) 2006-2007, Ext JS, LLC.
5045  *
5046  * Originally Released Under LGPL - original licence link has changed is not relivant.
5047  *
5048  * Fork - LGPL
5049  * <script type="text/javascript">
5050  */
5051  
5052
5053 /**
5054  * @class Roo.ColorPalette
5055  * @extends Roo.Component
5056  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5057  * Here's an example of typical usage:
5058  * <pre><code>
5059 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5060 cp.render('my-div');
5061
5062 cp.on('select', function(palette, selColor){
5063     // do something with selColor
5064 });
5065 </code></pre>
5066  * @constructor
5067  * Create a new ColorPalette
5068  * @param {Object} config The config object
5069  */
5070 Roo.ColorPalette = function(config){
5071     Roo.ColorPalette.superclass.constructor.call(this, config);
5072     this.addEvents({
5073         /**
5074              * @event select
5075              * Fires when a color is selected
5076              * @param {ColorPalette} this
5077              * @param {String} color The 6-digit color hex code (without the # symbol)
5078              */
5079         select: true
5080     });
5081
5082     if(this.handler){
5083         this.on("select", this.handler, this.scope, true);
5084     }
5085 };
5086 Roo.extend(Roo.ColorPalette, Roo.Component, {
5087     /**
5088      * @cfg {String} itemCls
5089      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5090      */
5091     itemCls : "x-color-palette",
5092     /**
5093      * @cfg {String} value
5094      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5095      * the hex codes are case-sensitive.
5096      */
5097     value : null,
5098     clickEvent:'click',
5099     // private
5100     ctype: "Roo.ColorPalette",
5101
5102     /**
5103      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5104      */
5105     allowReselect : false,
5106
5107     /**
5108      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5109      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5110      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5111      * of colors with the width setting until the box is symmetrical.</p>
5112      * <p>You can override individual colors if needed:</p>
5113      * <pre><code>
5114 var cp = new Roo.ColorPalette();
5115 cp.colors[0] = "FF0000";  // change the first box to red
5116 </code></pre>
5117
5118 Or you can provide a custom array of your own for complete control:
5119 <pre><code>
5120 var cp = new Roo.ColorPalette();
5121 cp.colors = ["000000", "993300", "333300"];
5122 </code></pre>
5123      * @type Array
5124      */
5125     colors : [
5126         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5127         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5128         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5129         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5130         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5131     ],
5132
5133     // private
5134     onRender : function(container, position){
5135         var t = new Roo.MasterTemplate(
5136             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5137         );
5138         var c = this.colors;
5139         for(var i = 0, len = c.length; i < len; i++){
5140             t.add([c[i]]);
5141         }
5142         var el = document.createElement("div");
5143         el.className = this.itemCls;
5144         t.overwrite(el);
5145         container.dom.insertBefore(el, position);
5146         this.el = Roo.get(el);
5147         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5148         if(this.clickEvent != 'click'){
5149             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5150         }
5151     },
5152
5153     // private
5154     afterRender : function(){
5155         Roo.ColorPalette.superclass.afterRender.call(this);
5156         if(this.value){
5157             var s = this.value;
5158             this.value = null;
5159             this.select(s);
5160         }
5161     },
5162
5163     // private
5164     handleClick : function(e, t){
5165         e.preventDefault();
5166         if(!this.disabled){
5167             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5168             this.select(c.toUpperCase());
5169         }
5170     },
5171
5172     /**
5173      * Selects the specified color in the palette (fires the select event)
5174      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5175      */
5176     select : function(color){
5177         color = color.replace("#", "");
5178         if(color != this.value || this.allowReselect){
5179             var el = this.el;
5180             if(this.value){
5181                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5182             }
5183             el.child("a.color-"+color).addClass("x-color-palette-sel");
5184             this.value = color;
5185             this.fireEvent("select", this, color);
5186         }
5187     }
5188 });/*
5189  * Based on:
5190  * Ext JS Library 1.1.1
5191  * Copyright(c) 2006-2007, Ext JS, LLC.
5192  *
5193  * Originally Released Under LGPL - original licence link has changed is not relivant.
5194  *
5195  * Fork - LGPL
5196  * <script type="text/javascript">
5197  */
5198  
5199 /**
5200  * @class Roo.DatePicker
5201  * @extends Roo.Component
5202  * Simple date picker class.
5203  * @constructor
5204  * Create a new DatePicker
5205  * @param {Object} config The config object
5206  */
5207 Roo.DatePicker = function(config){
5208     Roo.DatePicker.superclass.constructor.call(this, config);
5209
5210     this.value = config && config.value ?
5211                  config.value.clearTime() : new Date().clearTime();
5212
5213     this.addEvents({
5214         /**
5215              * @event select
5216              * Fires when a date is selected
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected date
5219              */
5220         'select': true,
5221         /**
5222              * @event monthchange
5223              * Fires when the displayed month changes 
5224              * @param {DatePicker} this
5225              * @param {Date} date The selected month
5226              */
5227         'monthchange': true
5228     });
5229
5230     if(this.handler){
5231         this.on("select", this.handler,  this.scope || this);
5232     }
5233     // build the disabledDatesRE
5234     if(!this.disabledDatesRE && this.disabledDates){
5235         var dd = this.disabledDates;
5236         var re = "(?:";
5237         for(var i = 0; i < dd.length; i++){
5238             re += dd[i];
5239             if(i != dd.length-1) {
5240                 re += "|";
5241             }
5242         }
5243         this.disabledDatesRE = new RegExp(re + ")");
5244     }
5245 };
5246
5247 Roo.extend(Roo.DatePicker, Roo.Component, {
5248     /**
5249      * @cfg {String} todayText
5250      * The text to display on the button that selects the current date (defaults to "Today")
5251      */
5252     todayText : "Today",
5253     /**
5254      * @cfg {String} okText
5255      * The text to display on the ok button
5256      */
5257     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5258     /**
5259      * @cfg {String} cancelText
5260      * The text to display on the cancel button
5261      */
5262     cancelText : "Cancel",
5263     /**
5264      * @cfg {String} todayTip
5265      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5266      */
5267     todayTip : "{0} (Spacebar)",
5268     /**
5269      * @cfg {Date} minDate
5270      * Minimum allowable date (JavaScript date object, defaults to null)
5271      */
5272     minDate : null,
5273     /**
5274      * @cfg {Date} maxDate
5275      * Maximum allowable date (JavaScript date object, defaults to null)
5276      */
5277     maxDate : null,
5278     /**
5279      * @cfg {String} minText
5280      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5281      */
5282     minText : "This date is before the minimum date",
5283     /**
5284      * @cfg {String} maxText
5285      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5286      */
5287     maxText : "This date is after the maximum date",
5288     /**
5289      * @cfg {String} format
5290      * The default date format string which can be overriden for localization support.  The format must be
5291      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5292      */
5293     format : "m/d/y",
5294     /**
5295      * @cfg {Array} disabledDays
5296      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5297      */
5298     disabledDays : null,
5299     /**
5300      * @cfg {String} disabledDaysText
5301      * The tooltip to display when the date falls on a disabled day (defaults to "")
5302      */
5303     disabledDaysText : "",
5304     /**
5305      * @cfg {RegExp} disabledDatesRE
5306      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5307      */
5308     disabledDatesRE : null,
5309     /**
5310      * @cfg {String} disabledDatesText
5311      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5312      */
5313     disabledDatesText : "",
5314     /**
5315      * @cfg {Boolean} constrainToViewport
5316      * True to constrain the date picker to the viewport (defaults to true)
5317      */
5318     constrainToViewport : true,
5319     /**
5320      * @cfg {Array} monthNames
5321      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5322      */
5323     monthNames : Date.monthNames,
5324     /**
5325      * @cfg {Array} dayNames
5326      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5327      */
5328     dayNames : Date.dayNames,
5329     /**
5330      * @cfg {String} nextText
5331      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5332      */
5333     nextText: 'Next Month (Control+Right)',
5334     /**
5335      * @cfg {String} prevText
5336      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5337      */
5338     prevText: 'Previous Month (Control+Left)',
5339     /**
5340      * @cfg {String} monthYearText
5341      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5342      */
5343     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5344     /**
5345      * @cfg {Number} startDay
5346      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5347      */
5348     startDay : 0,
5349     /**
5350      * @cfg {Bool} showClear
5351      * Show a clear button (usefull for date form elements that can be blank.)
5352      */
5353     
5354     showClear: false,
5355     
5356     /**
5357      * Sets the value of the date field
5358      * @param {Date} value The date to set
5359      */
5360     setValue : function(value){
5361         var old = this.value;
5362         
5363         if (typeof(value) == 'string') {
5364          
5365             value = Date.parseDate(value, this.format);
5366         }
5367         if (!value) {
5368             value = new Date();
5369         }
5370         
5371         this.value = value.clearTime(true);
5372         if(this.el){
5373             this.update(this.value);
5374         }
5375     },
5376
5377     /**
5378      * Gets the current selected value of the date field
5379      * @return {Date} The selected date
5380      */
5381     getValue : function(){
5382         return this.value;
5383     },
5384
5385     // private
5386     focus : function(){
5387         if(this.el){
5388             this.update(this.activeDate);
5389         }
5390     },
5391
5392     // privateval
5393     onRender : function(container, position){
5394         
5395         var m = [
5396              '<table cellspacing="0">',
5397                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5398                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5399         var dn = this.dayNames;
5400         for(var i = 0; i < 7; i++){
5401             var d = this.startDay+i;
5402             if(d > 6){
5403                 d = d-7;
5404             }
5405             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5406         }
5407         m[m.length] = "</tr></thead><tbody><tr>";
5408         for(var i = 0; i < 42; i++) {
5409             if(i % 7 == 0 && i != 0){
5410                 m[m.length] = "</tr><tr>";
5411             }
5412             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5413         }
5414         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5415             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5416
5417         var el = document.createElement("div");
5418         el.className = "x-date-picker";
5419         el.innerHTML = m.join("");
5420
5421         container.dom.insertBefore(el, position);
5422
5423         this.el = Roo.get(el);
5424         this.eventEl = Roo.get(el.firstChild);
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5427             handler: this.showPrevMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5434             handler: this.showNextMonth,
5435             scope: this,
5436             preventDefault:true,
5437             stopDefault:true
5438         });
5439
5440         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5441
5442         this.monthPicker = this.el.down('div.x-date-mp');
5443         this.monthPicker.enableDisplayMode('block');
5444         
5445         var kn = new Roo.KeyNav(this.eventEl, {
5446             "left" : function(e){
5447                 e.ctrlKey ?
5448                     this.showPrevMonth() :
5449                     this.update(this.activeDate.add("d", -1));
5450             },
5451
5452             "right" : function(e){
5453                 e.ctrlKey ?
5454                     this.showNextMonth() :
5455                     this.update(this.activeDate.add("d", 1));
5456             },
5457
5458             "up" : function(e){
5459                 e.ctrlKey ?
5460                     this.showNextYear() :
5461                     this.update(this.activeDate.add("d", -7));
5462             },
5463
5464             "down" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevYear() :
5467                     this.update(this.activeDate.add("d", 7));
5468             },
5469
5470             "pageUp" : function(e){
5471                 this.showNextMonth();
5472             },
5473
5474             "pageDown" : function(e){
5475                 this.showPrevMonth();
5476             },
5477
5478             "enter" : function(e){
5479                 e.stopPropagation();
5480                 return true;
5481             },
5482
5483             scope : this
5484         });
5485
5486         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5487
5488         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5489
5490         this.el.unselectable();
5491         
5492         this.cells = this.el.select("table.x-date-inner tbody td");
5493         this.textNodes = this.el.query("table.x-date-inner tbody span");
5494
5495         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5496             text: "&#160;",
5497             tooltip: this.monthYearText
5498         });
5499
5500         this.mbtn.on('click', this.showMonthPicker, this);
5501         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5502
5503
5504         var today = (new Date()).dateFormat(this.format);
5505         
5506         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5507         if (this.showClear) {
5508             baseTb.add( new Roo.Toolbar.Fill());
5509         }
5510         baseTb.add({
5511             text: String.format(this.todayText, today),
5512             tooltip: String.format(this.todayTip, today),
5513             handler: this.selectToday,
5514             scope: this
5515         });
5516         
5517         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5518             
5519         //});
5520         if (this.showClear) {
5521             
5522             baseTb.add( new Roo.Toolbar.Fill());
5523             baseTb.add({
5524                 text: '&#160;',
5525                 cls: 'x-btn-icon x-btn-clear',
5526                 handler: function() {
5527                     //this.value = '';
5528                     this.fireEvent("select", this, '');
5529                 },
5530                 scope: this
5531             });
5532         }
5533         
5534         
5535         if(Roo.isIE){
5536             this.el.repaint();
5537         }
5538         this.update(this.value);
5539     },
5540
5541     createMonthPicker : function(){
5542         if(!this.monthPicker.dom.firstChild){
5543             var buf = ['<table border="0" cellspacing="0">'];
5544             for(var i = 0; i < 6; i++){
5545                 buf.push(
5546                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5547                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5548                     i == 0 ?
5549                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5550                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5551                 );
5552             }
5553             buf.push(
5554                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5555                     this.okText,
5556                     '</button><button type="button" class="x-date-mp-cancel">',
5557                     this.cancelText,
5558                     '</button></td></tr>',
5559                 '</table>'
5560             );
5561             this.monthPicker.update(buf.join(''));
5562             this.monthPicker.on('click', this.onMonthClick, this);
5563             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5564
5565             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5566             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5567
5568             this.mpMonths.each(function(m, a, i){
5569                 i += 1;
5570                 if((i%2) == 0){
5571                     m.dom.xmonth = 5 + Math.round(i * .5);
5572                 }else{
5573                     m.dom.xmonth = Math.round((i-1) * .5);
5574                 }
5575             });
5576         }
5577     },
5578
5579     showMonthPicker : function(){
5580         this.createMonthPicker();
5581         var size = this.el.getSize();
5582         this.monthPicker.setSize(size);
5583         this.monthPicker.child('table').setSize(size);
5584
5585         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5586         this.updateMPMonth(this.mpSelMonth);
5587         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5588         this.updateMPYear(this.mpSelYear);
5589
5590         this.monthPicker.slideIn('t', {duration:.2});
5591     },
5592
5593     updateMPYear : function(y){
5594         this.mpyear = y;
5595         var ys = this.mpYears.elements;
5596         for(var i = 1; i <= 10; i++){
5597             var td = ys[i-1], y2;
5598             if((i%2) == 0){
5599                 y2 = y + Math.round(i * .5);
5600                 td.firstChild.innerHTML = y2;
5601                 td.xyear = y2;
5602             }else{
5603                 y2 = y - (5-Math.round(i * .5));
5604                 td.firstChild.innerHTML = y2;
5605                 td.xyear = y2;
5606             }
5607             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5608         }
5609     },
5610
5611     updateMPMonth : function(sm){
5612         this.mpMonths.each(function(m, a, i){
5613             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5614         });
5615     },
5616
5617     selectMPMonth: function(m){
5618         
5619     },
5620
5621     onMonthClick : function(e, t){
5622         e.stopEvent();
5623         var el = new Roo.Element(t), pn;
5624         if(el.is('button.x-date-mp-cancel')){
5625             this.hideMonthPicker();
5626         }
5627         else if(el.is('button.x-date-mp-ok')){
5628             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5629             this.hideMonthPicker();
5630         }
5631         else if(pn = el.up('td.x-date-mp-month', 2)){
5632             this.mpMonths.removeClass('x-date-mp-sel');
5633             pn.addClass('x-date-mp-sel');
5634             this.mpSelMonth = pn.dom.xmonth;
5635         }
5636         else if(pn = el.up('td.x-date-mp-year', 2)){
5637             this.mpYears.removeClass('x-date-mp-sel');
5638             pn.addClass('x-date-mp-sel');
5639             this.mpSelYear = pn.dom.xyear;
5640         }
5641         else if(el.is('a.x-date-mp-prev')){
5642             this.updateMPYear(this.mpyear-10);
5643         }
5644         else if(el.is('a.x-date-mp-next')){
5645             this.updateMPYear(this.mpyear+10);
5646         }
5647     },
5648
5649     onMonthDblClick : function(e, t){
5650         e.stopEvent();
5651         var el = new Roo.Element(t), pn;
5652         if(pn = el.up('td.x-date-mp-month', 2)){
5653             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5654             this.hideMonthPicker();
5655         }
5656         else if(pn = el.up('td.x-date-mp-year', 2)){
5657             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5658             this.hideMonthPicker();
5659         }
5660     },
5661
5662     hideMonthPicker : function(disableAnim){
5663         if(this.monthPicker){
5664             if(disableAnim === true){
5665                 this.monthPicker.hide();
5666             }else{
5667                 this.monthPicker.slideOut('t', {duration:.2});
5668             }
5669         }
5670     },
5671
5672     // private
5673     showPrevMonth : function(e){
5674         this.update(this.activeDate.add("mo", -1));
5675     },
5676
5677     // private
5678     showNextMonth : function(e){
5679         this.update(this.activeDate.add("mo", 1));
5680     },
5681
5682     // private
5683     showPrevYear : function(){
5684         this.update(this.activeDate.add("y", -1));
5685     },
5686
5687     // private
5688     showNextYear : function(){
5689         this.update(this.activeDate.add("y", 1));
5690     },
5691
5692     // private
5693     handleMouseWheel : function(e){
5694         var delta = e.getWheelDelta();
5695         if(delta > 0){
5696             this.showPrevMonth();
5697             e.stopEvent();
5698         } else if(delta < 0){
5699             this.showNextMonth();
5700             e.stopEvent();
5701         }
5702     },
5703
5704     // private
5705     handleDateClick : function(e, t){
5706         e.stopEvent();
5707         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5708             this.setValue(new Date(t.dateValue));
5709             this.fireEvent("select", this, this.value);
5710         }
5711     },
5712
5713     // private
5714     selectToday : function(){
5715         this.setValue(new Date().clearTime());
5716         this.fireEvent("select", this, this.value);
5717     },
5718
5719     // private
5720     update : function(date)
5721     {
5722         var vd = this.activeDate;
5723         this.activeDate = date;
5724         if(vd && this.el){
5725             var t = date.getTime();
5726             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5727                 this.cells.removeClass("x-date-selected");
5728                 this.cells.each(function(c){
5729                    if(c.dom.firstChild.dateValue == t){
5730                        c.addClass("x-date-selected");
5731                        setTimeout(function(){
5732                             try{c.dom.firstChild.focus();}catch(e){}
5733                        }, 50);
5734                        return false;
5735                    }
5736                 });
5737                 return;
5738             }
5739         }
5740         
5741         var days = date.getDaysInMonth();
5742         var firstOfMonth = date.getFirstDateOfMonth();
5743         var startingPos = firstOfMonth.getDay()-this.startDay;
5744
5745         if(startingPos <= this.startDay){
5746             startingPos += 7;
5747         }
5748
5749         var pm = date.add("mo", -1);
5750         var prevStart = pm.getDaysInMonth()-startingPos;
5751
5752         var cells = this.cells.elements;
5753         var textEls = this.textNodes;
5754         days += startingPos;
5755
5756         // convert everything to numbers so it's fast
5757         var day = 86400000;
5758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5759         var today = new Date().clearTime().getTime();
5760         var sel = date.clearTime().getTime();
5761         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5762         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5763         var ddMatch = this.disabledDatesRE;
5764         var ddText = this.disabledDatesText;
5765         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5766         var ddaysText = this.disabledDaysText;
5767         var format = this.format;
5768
5769         var setCellClass = function(cal, cell){
5770             cell.title = "";
5771             var t = d.getTime();
5772             cell.firstChild.dateValue = t;
5773             if(t == today){
5774                 cell.className += " x-date-today";
5775                 cell.title = cal.todayText;
5776             }
5777             if(t == sel){
5778                 cell.className += " x-date-selected";
5779                 setTimeout(function(){
5780                     try{cell.firstChild.focus();}catch(e){}
5781                 }, 50);
5782             }
5783             // disabling
5784             if(t < min) {
5785                 cell.className = " x-date-disabled";
5786                 cell.title = cal.minText;
5787                 return;
5788             }
5789             if(t > max) {
5790                 cell.className = " x-date-disabled";
5791                 cell.title = cal.maxText;
5792                 return;
5793             }
5794             if(ddays){
5795                 if(ddays.indexOf(d.getDay()) != -1){
5796                     cell.title = ddaysText;
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800             if(ddMatch && format){
5801                 var fvalue = d.dateFormat(format);
5802                 if(ddMatch.test(fvalue)){
5803                     cell.title = ddText.replace("%0", fvalue);
5804                     cell.className = " x-date-disabled";
5805                 }
5806             }
5807         };
5808
5809         var i = 0;
5810         for(; i < startingPos; i++) {
5811             textEls[i].innerHTML = (++prevStart);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-prevday";
5814             setCellClass(this, cells[i]);
5815         }
5816         for(; i < days; i++){
5817             intDay = i - startingPos + 1;
5818             textEls[i].innerHTML = (intDay);
5819             d.setDate(d.getDate()+1);
5820             cells[i].className = "x-date-active";
5821             setCellClass(this, cells[i]);
5822         }
5823         var extraDays = 0;
5824         for(; i < 42; i++) {
5825              textEls[i].innerHTML = (++extraDays);
5826              d.setDate(d.getDate()+1);
5827              cells[i].className = "x-date-nextday";
5828              setCellClass(this, cells[i]);
5829         }
5830
5831         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5832         this.fireEvent('monthchange', this, date);
5833         
5834         if(!this.internalRender){
5835             var main = this.el.dom.firstChild;
5836             var w = main.offsetWidth;
5837             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5838             Roo.fly(main).setWidth(w);
5839             this.internalRender = true;
5840             // opera does not respect the auto grow header center column
5841             // then, after it gets a width opera refuses to recalculate
5842             // without a second pass
5843             if(Roo.isOpera && !this.secondPass){
5844                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5845                 this.secondPass = true;
5846                 this.update.defer(10, this, [date]);
5847             }
5848         }
5849         
5850         
5851     }
5852 });        /*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862 /**
5863  * @class Roo.TabPanel
5864  * @extends Roo.util.Observable
5865  * A lightweight tab container.
5866  * <br><br>
5867  * Usage:
5868  * <pre><code>
5869 // basic tabs 1, built from existing content
5870 var tabs = new Roo.TabPanel("tabs1");
5871 tabs.addTab("script", "View Script");
5872 tabs.addTab("markup", "View Markup");
5873 tabs.activate("script");
5874
5875 // more advanced tabs, built from javascript
5876 var jtabs = new Roo.TabPanel("jtabs");
5877 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5878
5879 // set up the UpdateManager
5880 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5881 var updater = tab2.getUpdateManager();
5882 updater.setDefaultUrl("ajax1.htm");
5883 tab2.on('activate', updater.refresh, updater, true);
5884
5885 // Use setUrl for Ajax loading
5886 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5887 tab3.setUrl("ajax2.htm", null, true);
5888
5889 // Disabled tab
5890 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5891 tab4.disable();
5892
5893 jtabs.activate("jtabs-1");
5894  * </code></pre>
5895  * @constructor
5896  * Create a new TabPanel.
5897  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5898  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5899  */
5900 Roo.TabPanel = function(container, config){
5901     /**
5902     * The container element for this TabPanel.
5903     * @type Roo.Element
5904     */
5905     this.el = Roo.get(container, true);
5906     if(config){
5907         if(typeof config == "boolean"){
5908             this.tabPosition = config ? "bottom" : "top";
5909         }else{
5910             Roo.apply(this, config);
5911         }
5912     }
5913     if(this.tabPosition == "bottom"){
5914         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5915         this.el.addClass("x-tabs-bottom");
5916     }
5917     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5918     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5919     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5920     if(Roo.isIE){
5921         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5922     }
5923     if(this.tabPosition != "bottom"){
5924         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5925          * @type Roo.Element
5926          */
5927         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928         this.el.addClass("x-tabs-top");
5929     }
5930     this.items = [];
5931
5932     this.bodyEl.setStyle("position", "relative");
5933
5934     this.active = null;
5935     this.activateDelegate = this.activate.createDelegate(this);
5936
5937     this.addEvents({
5938         /**
5939          * @event tabchange
5940          * Fires when the active tab changes
5941          * @param {Roo.TabPanel} this
5942          * @param {Roo.TabPanelItem} activePanel The new active tab
5943          */
5944         "tabchange": true,
5945         /**
5946          * @event beforetabchange
5947          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5948          * @param {Roo.TabPanel} this
5949          * @param {Object} e Set cancel to true on this object to cancel the tab change
5950          * @param {Roo.TabPanelItem} tab The tab being changed to
5951          */
5952         "beforetabchange" : true
5953     });
5954
5955     Roo.EventManager.onWindowResize(this.onResize, this);
5956     this.cpad = this.el.getPadding("lr");
5957     this.hiddenCount = 0;
5958
5959
5960     // toolbar on the tabbar support...
5961     if (this.toolbar) {
5962         var tcfg = this.toolbar;
5963         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5964         this.toolbar = new Roo.Toolbar(tcfg);
5965         if (Roo.isSafari) {
5966             var tbl = tcfg.container.child('table', true);
5967             tbl.setAttribute('width', '100%');
5968         }
5969         
5970     }
5971    
5972
5973
5974     Roo.TabPanel.superclass.constructor.call(this);
5975 };
5976
5977 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5978     /*
5979      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5980      */
5981     tabPosition : "top",
5982     /*
5983      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5984      */
5985     currentTabWidth : 0,
5986     /*
5987      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     minTabWidth : 40,
5990     /*
5991      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5992      */
5993     maxTabWidth : 250,
5994     /*
5995      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5996      */
5997     preferredTabWidth : 175,
5998     /*
5999      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6000      */
6001     resizeTabs : false,
6002     /*
6003      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6004      */
6005     monitorResize : true,
6006     /*
6007      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6008      */
6009     toolbar : false,
6010
6011     /**
6012      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6013      * @param {String} id The id of the div to use <b>or create</b>
6014      * @param {String} text The text for the tab
6015      * @param {String} content (optional) Content to put in the TabPanelItem body
6016      * @param {Boolean} closable (optional) True to create a close icon on the tab
6017      * @return {Roo.TabPanelItem} The created TabPanelItem
6018      */
6019     addTab : function(id, text, content, closable){
6020         var item = new Roo.TabPanelItem(this, id, text, closable);
6021         this.addTabItem(item);
6022         if(content){
6023             item.setContent(content);
6024         }
6025         return item;
6026     },
6027
6028     /**
6029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6031      * @return {Roo.TabPanelItem}
6032      */
6033     getTab : function(id){
6034         return this.items[id];
6035     },
6036
6037     /**
6038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6040      */
6041     hideTab : function(id){
6042         var t = this.items[id];
6043         if(!t.isHidden()){
6044            t.setHidden(true);
6045            this.hiddenCount++;
6046            this.autoSizeTabs();
6047         }
6048     },
6049
6050     /**
6051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6053      */
6054     unhideTab : function(id){
6055         var t = this.items[id];
6056         if(t.isHidden()){
6057            t.setHidden(false);
6058            this.hiddenCount--;
6059            this.autoSizeTabs();
6060         }
6061     },
6062
6063     /**
6064      * Adds an existing {@link Roo.TabPanelItem}.
6065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6066      */
6067     addTabItem : function(item){
6068         this.items[item.id] = item;
6069         this.items.push(item);
6070         if(this.resizeTabs){
6071            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6072            this.autoSizeTabs();
6073         }else{
6074             item.autoSize();
6075         }
6076     },
6077
6078     /**
6079      * Removes a {@link Roo.TabPanelItem}.
6080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6081      */
6082     removeTab : function(id){
6083         var items = this.items;
6084         var tab = items[id];
6085         if(!tab) { return; }
6086         var index = items.indexOf(tab);
6087         if(this.active == tab && items.length > 1){
6088             var newTab = this.getNextAvailable(index);
6089             if(newTab) {
6090                 newTab.activate();
6091             }
6092         }
6093         this.stripEl.dom.removeChild(tab.pnode.dom);
6094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6096         }
6097         items.splice(index, 1);
6098         delete this.items[tab.id];
6099         tab.fireEvent("close", tab);
6100         tab.purgeListeners();
6101         this.autoSizeTabs();
6102     },
6103
6104     getNextAvailable : function(start){
6105         var items = this.items;
6106         var index = start;
6107         // look for a next tab that will slide over to
6108         // replace the one being removed
6109         while(index < items.length){
6110             var item = items[++index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         // if one isn't found select the previous tab (on the left)
6116         index = start;
6117         while(index >= 0){
6118             var item = items[--index];
6119             if(item && !item.isHidden()){
6120                 return item;
6121             }
6122         }
6123         return null;
6124     },
6125
6126     /**
6127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6129      */
6130     disableTab : function(id){
6131         var tab = this.items[id];
6132         if(tab && this.active != tab){
6133             tab.disable();
6134         }
6135     },
6136
6137     /**
6138      * Enables a {@link Roo.TabPanelItem} that is disabled.
6139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6140      */
6141     enableTab : function(id){
6142         var tab = this.items[id];
6143         tab.enable();
6144     },
6145
6146     /**
6147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6149      * @return {Roo.TabPanelItem} The TabPanelItem.
6150      */
6151     activate : function(id){
6152         var tab = this.items[id];
6153         if(!tab){
6154             return null;
6155         }
6156         if(tab == this.active || tab.disabled){
6157             return tab;
6158         }
6159         var e = {};
6160         this.fireEvent("beforetabchange", this, e, tab);
6161         if(e.cancel !== true && !tab.disabled){
6162             if(this.active){
6163                 this.active.hide();
6164             }
6165             this.active = this.items[id];
6166             this.active.show();
6167             this.fireEvent("tabchange", this, this.active);
6168         }
6169         return tab;
6170     },
6171
6172     /**
6173      * Gets the active {@link Roo.TabPanelItem}.
6174      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6175      */
6176     getActiveTab : function(){
6177         return this.active;
6178     },
6179
6180     /**
6181      * Updates the tab body element to fit the height of the container element
6182      * for overflow scrolling
6183      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6184      */
6185     syncHeight : function(targetHeight){
6186         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6187         var bm = this.bodyEl.getMargins();
6188         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6189         this.bodyEl.setHeight(newHeight);
6190         return newHeight;
6191     },
6192
6193     onResize : function(){
6194         if(this.monitorResize){
6195             this.autoSizeTabs();
6196         }
6197     },
6198
6199     /**
6200      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     beginUpdate : function(){
6203         this.updating = true;
6204     },
6205
6206     /**
6207      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     endUpdate : function(){
6210         this.updating = false;
6211         this.autoSizeTabs();
6212     },
6213
6214     /**
6215      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6216      */
6217     autoSizeTabs : function(){
6218         var count = this.items.length;
6219         var vcount = count - this.hiddenCount;
6220         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6221             return;
6222         }
6223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6224         var availWidth = Math.floor(w / vcount);
6225         var b = this.stripBody;
6226         if(b.getWidth() > w){
6227             var tabs = this.items;
6228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6229             if(availWidth < this.minTabWidth){
6230                 /*if(!this.sleft){    // incomplete scrolling code
6231                     this.createScrollButtons();
6232                 }
6233                 this.showScroll();
6234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6235             }
6236         }else{
6237             if(this.currentTabWidth < this.preferredTabWidth){
6238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6239             }
6240         }
6241     },
6242
6243     /**
6244      * Returns the number of tabs in this TabPanel.
6245      * @return {Number}
6246      */
6247      getCount : function(){
6248          return this.items.length;
6249      },
6250
6251     /**
6252      * Resizes all the tabs to the passed width
6253      * @param {Number} The new width
6254      */
6255     setTabWidth : function(width){
6256         this.currentTabWidth = width;
6257         for(var i = 0, len = this.items.length; i < len; i++) {
6258                 if(!this.items[i].isHidden()) {
6259                 this.items[i].setWidth(width);
6260             }
6261         }
6262     },
6263
6264     /**
6265      * Destroys this TabPanel
6266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6267      */
6268     destroy : function(removeEl){
6269         Roo.EventManager.removeResizeListener(this.onResize, this);
6270         for(var i = 0, len = this.items.length; i < len; i++){
6271             this.items[i].purgeListeners();
6272         }
6273         if(removeEl === true){
6274             this.el.update("");
6275             this.el.remove();
6276         }
6277     }
6278 });
6279
6280 /**
6281  * @class Roo.TabPanelItem
6282  * @extends Roo.util.Observable
6283  * Represents an individual item (tab plus body) in a TabPanel.
6284  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6285  * @param {String} id The id of this TabPanelItem
6286  * @param {String} text The text for the tab of this TabPanelItem
6287  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6288  */
6289 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6290     /**
6291      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6292      * @type Roo.TabPanel
6293      */
6294     this.tabPanel = tabPanel;
6295     /**
6296      * The id for this TabPanelItem
6297      * @type String
6298      */
6299     this.id = id;
6300     /** @private */
6301     this.disabled = false;
6302     /** @private */
6303     this.text = text;
6304     /** @private */
6305     this.loaded = false;
6306     this.closable = closable;
6307
6308     /**
6309      * The body element for this TabPanelItem.
6310      * @type Roo.Element
6311      */
6312     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6313     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6314     this.bodyEl.setStyle("display", "block");
6315     this.bodyEl.setStyle("zoom", "1");
6316     this.hideAction();
6317
6318     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6319     /** @private */
6320     this.el = Roo.get(els.el, true);
6321     this.inner = Roo.get(els.inner, true);
6322     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6323     this.pnode = Roo.get(els.el.parentNode, true);
6324     this.el.on("mousedown", this.onTabMouseDown, this);
6325     this.el.on("click", this.onTabClick, this);
6326     /** @private */
6327     if(closable){
6328         var c = Roo.get(els.close, true);
6329         c.dom.title = this.closeText;
6330         c.addClassOnOver("close-over");
6331         c.on("click", this.closeClick, this);
6332      }
6333
6334     this.addEvents({
6335          /**
6336          * @event activate
6337          * Fires when this tab becomes the active tab.
6338          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6339          * @param {Roo.TabPanelItem} this
6340          */
6341         "activate": true,
6342         /**
6343          * @event beforeclose
6344          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6345          * @param {Roo.TabPanelItem} this
6346          * @param {Object} e Set cancel to true on this object to cancel the close.
6347          */
6348         "beforeclose": true,
6349         /**
6350          * @event close
6351          * Fires when this tab is closed.
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "close": true,
6355         /**
6356          * @event deactivate
6357          * Fires when this tab is no longer the active tab.
6358          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6359          * @param {Roo.TabPanelItem} this
6360          */
6361          "deactivate" : true
6362     });
6363     this.hidden = false;
6364
6365     Roo.TabPanelItem.superclass.constructor.call(this);
6366 };
6367
6368 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6369     purgeListeners : function(){
6370        Roo.util.Observable.prototype.purgeListeners.call(this);
6371        this.el.removeAllListeners();
6372     },
6373     /**
6374      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6375      */
6376     show : function(){
6377         this.pnode.addClass("on");
6378         this.showAction();
6379         if(Roo.isOpera){
6380             this.tabPanel.stripWrap.repaint();
6381         }
6382         this.fireEvent("activate", this.tabPanel, this);
6383     },
6384
6385     /**
6386      * Returns true if this tab is the active tab.
6387      * @return {Boolean}
6388      */
6389     isActive : function(){
6390         return this.tabPanel.getActiveTab() == this;
6391     },
6392
6393     /**
6394      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6395      */
6396     hide : function(){
6397         this.pnode.removeClass("on");
6398         this.hideAction();
6399         this.fireEvent("deactivate", this.tabPanel, this);
6400     },
6401
6402     hideAction : function(){
6403         this.bodyEl.hide();
6404         this.bodyEl.setStyle("position", "absolute");
6405         this.bodyEl.setLeft("-20000px");
6406         this.bodyEl.setTop("-20000px");
6407     },
6408
6409     showAction : function(){
6410         this.bodyEl.setStyle("position", "relative");
6411         this.bodyEl.setTop("");
6412         this.bodyEl.setLeft("");
6413         this.bodyEl.show();
6414     },
6415
6416     /**
6417      * Set the tooltip for the tab.
6418      * @param {String} tooltip The tab's tooltip
6419      */
6420     setTooltip : function(text){
6421         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6422             this.textEl.dom.qtip = text;
6423             this.textEl.dom.removeAttribute('title');
6424         }else{
6425             this.textEl.dom.title = text;
6426         }
6427     },
6428
6429     onTabClick : function(e){
6430         e.preventDefault();
6431         this.tabPanel.activate(this.id);
6432     },
6433
6434     onTabMouseDown : function(e){
6435         e.preventDefault();
6436         this.tabPanel.activate(this.id);
6437     },
6438
6439     getWidth : function(){
6440         return this.inner.getWidth();
6441     },
6442
6443     setWidth : function(width){
6444         var iwidth = width - this.pnode.getPadding("lr");
6445         this.inner.setWidth(iwidth);
6446         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6447         this.pnode.setWidth(width);
6448     },
6449
6450     /**
6451      * Show or hide the tab
6452      * @param {Boolean} hidden True to hide or false to show.
6453      */
6454     setHidden : function(hidden){
6455         this.hidden = hidden;
6456         this.pnode.setStyle("display", hidden ? "none" : "");
6457     },
6458
6459     /**
6460      * Returns true if this tab is "hidden"
6461      * @return {Boolean}
6462      */
6463     isHidden : function(){
6464         return this.hidden;
6465     },
6466
6467     /**
6468      * Returns the text for this tab
6469      * @return {String}
6470      */
6471     getText : function(){
6472         return this.text;
6473     },
6474
6475     autoSize : function(){
6476         //this.el.beginMeasure();
6477         this.textEl.setWidth(1);
6478         /*
6479          *  #2804 [new] Tabs in Roojs
6480          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6481          */
6482         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6483         //this.el.endMeasure();
6484     },
6485
6486     /**
6487      * Sets the text for the tab (Note: this also sets the tooltip text)
6488      * @param {String} text The tab's text and tooltip
6489      */
6490     setText : function(text){
6491         this.text = text;
6492         this.textEl.update(text);
6493         this.setTooltip(text);
6494         if(!this.tabPanel.resizeTabs){
6495             this.autoSize();
6496         }
6497     },
6498     /**
6499      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6500      */
6501     activate : function(){
6502         this.tabPanel.activate(this.id);
6503     },
6504
6505     /**
6506      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6507      */
6508     disable : function(){
6509         if(this.tabPanel.active != this){
6510             this.disabled = true;
6511             this.pnode.addClass("disabled");
6512         }
6513     },
6514
6515     /**
6516      * Enables this TabPanelItem if it was previously disabled.
6517      */
6518     enable : function(){
6519         this.disabled = false;
6520         this.pnode.removeClass("disabled");
6521     },
6522
6523     /**
6524      * Sets the content for this TabPanelItem.
6525      * @param {String} content The content
6526      * @param {Boolean} loadScripts true to look for and load scripts
6527      */
6528     setContent : function(content, loadScripts){
6529         this.bodyEl.update(content, loadScripts);
6530     },
6531
6532     /**
6533      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6534      * @return {Roo.UpdateManager} The UpdateManager
6535      */
6536     getUpdateManager : function(){
6537         return this.bodyEl.getUpdateManager();
6538     },
6539
6540     /**
6541      * Set a URL to be used to load the content for this TabPanelItem.
6542      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6543      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6544      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6545      * @return {Roo.UpdateManager} The UpdateManager
6546      */
6547     setUrl : function(url, params, loadOnce){
6548         if(this.refreshDelegate){
6549             this.un('activate', this.refreshDelegate);
6550         }
6551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6552         this.on("activate", this.refreshDelegate);
6553         return this.bodyEl.getUpdateManager();
6554     },
6555
6556     /** @private */
6557     _handleRefresh : function(url, params, loadOnce){
6558         if(!loadOnce || !this.loaded){
6559             var updater = this.bodyEl.getUpdateManager();
6560             updater.update(url, params, this._setLoaded.createDelegate(this));
6561         }
6562     },
6563
6564     /**
6565      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6566      *   Will fail silently if the setUrl method has not been called.
6567      *   This does not activate the panel, just updates its content.
6568      */
6569     refresh : function(){
6570         if(this.refreshDelegate){
6571            this.loaded = false;
6572            this.refreshDelegate();
6573         }
6574     },
6575
6576     /** @private */
6577     _setLoaded : function(){
6578         this.loaded = true;
6579     },
6580
6581     /** @private */
6582     closeClick : function(e){
6583         var o = {};
6584         e.stopEvent();
6585         this.fireEvent("beforeclose", this, o);
6586         if(o.cancel !== true){
6587             this.tabPanel.removeTab(this.id);
6588         }
6589     },
6590     /**
6591      * The text displayed in the tooltip for the close icon.
6592      * @type String
6593      */
6594     closeText : "Close this tab"
6595 });
6596
6597 /** @private */
6598 Roo.TabPanel.prototype.createStrip = function(container){
6599     var strip = document.createElement("div");
6600     strip.className = "x-tabs-wrap";
6601     container.appendChild(strip);
6602     return strip;
6603 };
6604 /** @private */
6605 Roo.TabPanel.prototype.createStripList = function(strip){
6606     // div wrapper for retard IE
6607     // returns the "tr" element.
6608     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6609         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6610         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6611     return strip.firstChild.firstChild.firstChild.firstChild;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createBody = function(container){
6615     var body = document.createElement("div");
6616     Roo.id(body, "tab-body");
6617     Roo.fly(body).addClass("x-tabs-body");
6618     container.appendChild(body);
6619     return body;
6620 };
6621 /** @private */
6622 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6623     var body = Roo.getDom(id);
6624     if(!body){
6625         body = document.createElement("div");
6626         body.id = id;
6627     }
6628     Roo.fly(body).addClass("x-tabs-item-body");
6629     bodyEl.insertBefore(body, bodyEl.firstChild);
6630     return body;
6631 };
6632 /** @private */
6633 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6634     var td = document.createElement("td");
6635     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6636     //stripEl.appendChild(td);
6637     if(closable){
6638         td.className = "x-tabs-closable";
6639         if(!this.closeTpl){
6640             this.closeTpl = new Roo.Template(
6641                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6642                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6643                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6644             );
6645         }
6646         var el = this.closeTpl.overwrite(td, {"text": text});
6647         var close = el.getElementsByTagName("div")[0];
6648         var inner = el.getElementsByTagName("em")[0];
6649         return {"el": el, "close": close, "inner": inner};
6650     } else {
6651         if(!this.tabTpl){
6652             this.tabTpl = new Roo.Template(
6653                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6654                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6655             );
6656         }
6657         var el = this.tabTpl.overwrite(td, {"text": text});
6658         var inner = el.getElementsByTagName("em")[0];
6659         return {"el": el, "inner": inner};
6660     }
6661 };/*
6662  * Based on:
6663  * Ext JS Library 1.1.1
6664  * Copyright(c) 2006-2007, Ext JS, LLC.
6665  *
6666  * Originally Released Under LGPL - original licence link has changed is not relivant.
6667  *
6668  * Fork - LGPL
6669  * <script type="text/javascript">
6670  */
6671
6672 /**
6673  * @class Roo.Button
6674  * @extends Roo.util.Observable
6675  * Simple Button class
6676  * @cfg {String} text The button text
6677  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6678  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6679  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6680  * @cfg {Object} scope The scope of the handler
6681  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6682  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6683  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6684  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6685  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6686  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6687    applies if enableToggle = true)
6688  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6689  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6690   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6691  * @constructor
6692  * Create a new button
6693  * @param {Object} config The config object
6694  */
6695 Roo.Button = function(renderTo, config)
6696 {
6697     if (!config) {
6698         config = renderTo;
6699         renderTo = config.renderTo || false;
6700     }
6701     
6702     Roo.apply(this, config);
6703     this.addEvents({
6704         /**
6705              * @event click
6706              * Fires when this button is clicked
6707              * @param {Button} this
6708              * @param {EventObject} e The click event
6709              */
6710             "click" : true,
6711         /**
6712              * @event toggle
6713              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6714              * @param {Button} this
6715              * @param {Boolean} pressed
6716              */
6717             "toggle" : true,
6718         /**
6719              * @event mouseover
6720              * Fires when the mouse hovers over the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseover' : true,
6725         /**
6726              * @event mouseout
6727              * Fires when the mouse exits the button
6728              * @param {Button} this
6729              * @param {Event} e The event object
6730              */
6731         'mouseout': true,
6732          /**
6733              * @event render
6734              * Fires when the button is rendered
6735              * @param {Button} this
6736              */
6737         'render': true
6738     });
6739     if(this.menu){
6740         this.menu = Roo.menu.MenuMgr.get(this.menu);
6741     }
6742     // register listeners first!!  - so render can be captured..
6743     Roo.util.Observable.call(this);
6744     if(renderTo){
6745         this.render(renderTo);
6746     }
6747     
6748   
6749 };
6750
6751 Roo.extend(Roo.Button, Roo.util.Observable, {
6752     /**
6753      * 
6754      */
6755     
6756     /**
6757      * Read-only. True if this button is hidden
6758      * @type Boolean
6759      */
6760     hidden : false,
6761     /**
6762      * Read-only. True if this button is disabled
6763      * @type Boolean
6764      */
6765     disabled : false,
6766     /**
6767      * Read-only. True if this button is pressed (only if enableToggle = true)
6768      * @type Boolean
6769      */
6770     pressed : false,
6771
6772     /**
6773      * @cfg {Number} tabIndex 
6774      * The DOM tabIndex for this button (defaults to undefined)
6775      */
6776     tabIndex : undefined,
6777
6778     /**
6779      * @cfg {Boolean} enableToggle
6780      * True to enable pressed/not pressed toggling (defaults to false)
6781      */
6782     enableToggle: false,
6783     /**
6784      * @cfg {Mixed} menu
6785      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6786      */
6787     menu : undefined,
6788     /**
6789      * @cfg {String} menuAlign
6790      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6791      */
6792     menuAlign : "tl-bl?",
6793
6794     /**
6795      * @cfg {String} iconCls
6796      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6797      */
6798     iconCls : undefined,
6799     /**
6800      * @cfg {String} type
6801      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6802      */
6803     type : 'button',
6804
6805     // private
6806     menuClassTarget: 'tr',
6807
6808     /**
6809      * @cfg {String} clickEvent
6810      * The type of event to map to the button's event handler (defaults to 'click')
6811      */
6812     clickEvent : 'click',
6813
6814     /**
6815      * @cfg {Boolean} handleMouseEvents
6816      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6817      */
6818     handleMouseEvents : true,
6819
6820     /**
6821      * @cfg {String} tooltipType
6822      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6823      */
6824     tooltipType : 'qtip',
6825
6826     /**
6827      * @cfg {String} cls
6828      * A CSS class to apply to the button's main element.
6829      */
6830     
6831     /**
6832      * @cfg {Roo.Template} template (Optional)
6833      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6834      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6835      * require code modifications if required elements (e.g. a button) aren't present.
6836      */
6837
6838     // private
6839     render : function(renderTo){
6840         var btn;
6841         if(this.hideParent){
6842             this.parentEl = Roo.get(renderTo);
6843         }
6844         if(!this.dhconfig){
6845             if(!this.template){
6846                 if(!Roo.Button.buttonTemplate){
6847                     // hideous table template
6848                     Roo.Button.buttonTemplate = new Roo.Template(
6849                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6850                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6851                         "</tr></tbody></table>");
6852                 }
6853                 this.template = Roo.Button.buttonTemplate;
6854             }
6855             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6856             var btnEl = btn.child("button:first");
6857             btnEl.on('focus', this.onFocus, this);
6858             btnEl.on('blur', this.onBlur, this);
6859             if(this.cls){
6860                 btn.addClass(this.cls);
6861             }
6862             if(this.icon){
6863                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6864             }
6865             if(this.iconCls){
6866                 btnEl.addClass(this.iconCls);
6867                 if(!this.cls){
6868                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6869                 }
6870             }
6871             if(this.tabIndex !== undefined){
6872                 btnEl.dom.tabIndex = this.tabIndex;
6873             }
6874             if(this.tooltip){
6875                 if(typeof this.tooltip == 'object'){
6876                     Roo.QuickTips.tips(Roo.apply({
6877                           target: btnEl.id
6878                     }, this.tooltip));
6879                 } else {
6880                     btnEl.dom[this.tooltipType] = this.tooltip;
6881                 }
6882             }
6883         }else{
6884             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6885         }
6886         this.el = btn;
6887         if(this.id){
6888             this.el.dom.id = this.el.id = this.id;
6889         }
6890         if(this.menu){
6891             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6892             this.menu.on("show", this.onMenuShow, this);
6893             this.menu.on("hide", this.onMenuHide, this);
6894         }
6895         btn.addClass("x-btn");
6896         if(Roo.isIE && !Roo.isIE7){
6897             this.autoWidth.defer(1, this);
6898         }else{
6899             this.autoWidth();
6900         }
6901         if(this.handleMouseEvents){
6902             btn.on("mouseover", this.onMouseOver, this);
6903             btn.on("mouseout", this.onMouseOut, this);
6904             btn.on("mousedown", this.onMouseDown, this);
6905         }
6906         btn.on(this.clickEvent, this.onClick, this);
6907         //btn.on("mouseup", this.onMouseUp, this);
6908         if(this.hidden){
6909             this.hide();
6910         }
6911         if(this.disabled){
6912             this.disable();
6913         }
6914         Roo.ButtonToggleMgr.register(this);
6915         if(this.pressed){
6916             this.el.addClass("x-btn-pressed");
6917         }
6918         if(this.repeat){
6919             var repeater = new Roo.util.ClickRepeater(btn,
6920                 typeof this.repeat == "object" ? this.repeat : {}
6921             );
6922             repeater.on("click", this.onClick,  this);
6923         }
6924         
6925         this.fireEvent('render', this);
6926         
6927     },
6928     /**
6929      * Returns the button's underlying element
6930      * @return {Roo.Element} The element
6931      */
6932     getEl : function(){
6933         return this.el;  
6934     },
6935     
6936     /**
6937      * Destroys this Button and removes any listeners.
6938      */
6939     destroy : function(){
6940         Roo.ButtonToggleMgr.unregister(this);
6941         this.el.removeAllListeners();
6942         this.purgeListeners();
6943         this.el.remove();
6944     },
6945
6946     // private
6947     autoWidth : function(){
6948         if(this.el){
6949             this.el.setWidth("auto");
6950             if(Roo.isIE7 && Roo.isStrict){
6951                 var ib = this.el.child('button');
6952                 if(ib && ib.getWidth() > 20){
6953                     ib.clip();
6954                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6955                 }
6956             }
6957             if(this.minWidth){
6958                 if(this.hidden){
6959                     this.el.beginMeasure();
6960                 }
6961                 if(this.el.getWidth() < this.minWidth){
6962                     this.el.setWidth(this.minWidth);
6963                 }
6964                 if(this.hidden){
6965                     this.el.endMeasure();
6966                 }
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Assigns this button's click handler
6973      * @param {Function} handler The function to call when the button is clicked
6974      * @param {Object} scope (optional) Scope for the function passed in
6975      */
6976     setHandler : function(handler, scope){
6977         this.handler = handler;
6978         this.scope = scope;  
6979     },
6980     
6981     /**
6982      * Sets this button's text
6983      * @param {String} text The button text
6984      */
6985     setText : function(text){
6986         this.text = text;
6987         if(this.el){
6988             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6989         }
6990         this.autoWidth();
6991     },
6992     
6993     /**
6994      * Gets the text for this button
6995      * @return {String} The button text
6996      */
6997     getText : function(){
6998         return this.text;  
6999     },
7000     
7001     /**
7002      * Show this button
7003      */
7004     show: function(){
7005         this.hidden = false;
7006         if(this.el){
7007             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7008         }
7009     },
7010     
7011     /**
7012      * Hide this button
7013      */
7014     hide: function(){
7015         this.hidden = true;
7016         if(this.el){
7017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7018         }
7019     },
7020     
7021     /**
7022      * Convenience function for boolean show/hide
7023      * @param {Boolean} visible True to show, false to hide
7024      */
7025     setVisible: function(visible){
7026         if(visible) {
7027             this.show();
7028         }else{
7029             this.hide();
7030         }
7031     },
7032     
7033     /**
7034      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7035      * @param {Boolean} state (optional) Force a particular state
7036      */
7037     toggle : function(state){
7038         state = state === undefined ? !this.pressed : state;
7039         if(state != this.pressed){
7040             if(state){
7041                 this.el.addClass("x-btn-pressed");
7042                 this.pressed = true;
7043                 this.fireEvent("toggle", this, true);
7044             }else{
7045                 this.el.removeClass("x-btn-pressed");
7046                 this.pressed = false;
7047                 this.fireEvent("toggle", this, false);
7048             }
7049             if(this.toggleHandler){
7050                 this.toggleHandler.call(this.scope || this, this, state);
7051             }
7052         }
7053     },
7054     
7055     /**
7056      * Focus the button
7057      */
7058     focus : function(){
7059         this.el.child('button:first').focus();
7060     },
7061     
7062     /**
7063      * Disable this button
7064      */
7065     disable : function(){
7066         if(this.el){
7067             this.el.addClass("x-btn-disabled");
7068         }
7069         this.disabled = true;
7070     },
7071     
7072     /**
7073      * Enable this button
7074      */
7075     enable : function(){
7076         if(this.el){
7077             this.el.removeClass("x-btn-disabled");
7078         }
7079         this.disabled = false;
7080     },
7081
7082     /**
7083      * Convenience function for boolean enable/disable
7084      * @param {Boolean} enabled True to enable, false to disable
7085      */
7086     setDisabled : function(v){
7087         this[v !== true ? "enable" : "disable"]();
7088     },
7089
7090     // private
7091     onClick : function(e)
7092     {
7093         if(e){
7094             e.preventDefault();
7095         }
7096         if(e.button != 0){
7097             return;
7098         }
7099         if(!this.disabled){
7100             if(this.enableToggle){
7101                 this.toggle();
7102             }
7103             if(this.menu && !this.menu.isVisible()){
7104                 this.menu.show(this.el, this.menuAlign);
7105             }
7106             this.fireEvent("click", this, e);
7107             if(this.handler){
7108                 this.el.removeClass("x-btn-over");
7109                 this.handler.call(this.scope || this, this, e);
7110             }
7111         }
7112     },
7113     // private
7114     onMouseOver : function(e){
7115         if(!this.disabled){
7116             this.el.addClass("x-btn-over");
7117             this.fireEvent('mouseover', this, e);
7118         }
7119     },
7120     // private
7121     onMouseOut : function(e){
7122         if(!e.within(this.el,  true)){
7123             this.el.removeClass("x-btn-over");
7124             this.fireEvent('mouseout', this, e);
7125         }
7126     },
7127     // private
7128     onFocus : function(e){
7129         if(!this.disabled){
7130             this.el.addClass("x-btn-focus");
7131         }
7132     },
7133     // private
7134     onBlur : function(e){
7135         this.el.removeClass("x-btn-focus");
7136     },
7137     // private
7138     onMouseDown : function(e){
7139         if(!this.disabled && e.button == 0){
7140             this.el.addClass("x-btn-click");
7141             Roo.get(document).on('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMouseUp : function(e){
7146         if(e.button == 0){
7147             this.el.removeClass("x-btn-click");
7148             Roo.get(document).un('mouseup', this.onMouseUp, this);
7149         }
7150     },
7151     // private
7152     onMenuShow : function(e){
7153         this.el.addClass("x-btn-menu-active");
7154     },
7155     // private
7156     onMenuHide : function(e){
7157         this.el.removeClass("x-btn-menu-active");
7158     }   
7159 });
7160
7161 // Private utility class used by Button
7162 Roo.ButtonToggleMgr = function(){
7163    var groups = {};
7164    
7165    function toggleGroup(btn, state){
7166        if(state){
7167            var g = groups[btn.toggleGroup];
7168            for(var i = 0, l = g.length; i < l; i++){
7169                if(g[i] != btn){
7170                    g[i].toggle(false);
7171                }
7172            }
7173        }
7174    }
7175    
7176    return {
7177        register : function(btn){
7178            if(!btn.toggleGroup){
7179                return;
7180            }
7181            var g = groups[btn.toggleGroup];
7182            if(!g){
7183                g = groups[btn.toggleGroup] = [];
7184            }
7185            g.push(btn);
7186            btn.on("toggle", toggleGroup);
7187        },
7188        
7189        unregister : function(btn){
7190            if(!btn.toggleGroup){
7191                return;
7192            }
7193            var g = groups[btn.toggleGroup];
7194            if(g){
7195                g.remove(btn);
7196                btn.un("toggle", toggleGroup);
7197            }
7198        }
7199    };
7200 }();/*
7201  * Based on:
7202  * Ext JS Library 1.1.1
7203  * Copyright(c) 2006-2007, Ext JS, LLC.
7204  *
7205  * Originally Released Under LGPL - original licence link has changed is not relivant.
7206  *
7207  * Fork - LGPL
7208  * <script type="text/javascript">
7209  */
7210  
7211 /**
7212  * @class Roo.SplitButton
7213  * @extends Roo.Button
7214  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7215  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7216  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7217  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7218  * @cfg {String} arrowTooltip The title attribute of the arrow
7219  * @constructor
7220  * Create a new menu button
7221  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7222  * @param {Object} config The config object
7223  */
7224 Roo.SplitButton = function(renderTo, config){
7225     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7226     /**
7227      * @event arrowclick
7228      * Fires when this button's arrow is clicked
7229      * @param {SplitButton} this
7230      * @param {EventObject} e The click event
7231      */
7232     this.addEvents({"arrowclick":true});
7233 };
7234
7235 Roo.extend(Roo.SplitButton, Roo.Button, {
7236     render : function(renderTo){
7237         // this is one sweet looking template!
7238         var tpl = new Roo.Template(
7239             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7240             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7241             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7242             "</tbody></table></td><td>",
7243             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7244             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
7245             "</tbody></table></td></tr></table>"
7246         );
7247         var btn = tpl.append(renderTo, [this.text, this.type], true);
7248         var btnEl = btn.child("button");
7249         if(this.cls){
7250             btn.addClass(this.cls);
7251         }
7252         if(this.icon){
7253             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7254         }
7255         if(this.iconCls){
7256             btnEl.addClass(this.iconCls);
7257             if(!this.cls){
7258                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7259             }
7260         }
7261         this.el = btn;
7262         if(this.handleMouseEvents){
7263             btn.on("mouseover", this.onMouseOver, this);
7264             btn.on("mouseout", this.onMouseOut, this);
7265             btn.on("mousedown", this.onMouseDown, this);
7266             btn.on("mouseup", this.onMouseUp, this);
7267         }
7268         btn.on(this.clickEvent, this.onClick, this);
7269         if(this.tooltip){
7270             if(typeof this.tooltip == 'object'){
7271                 Roo.QuickTips.tips(Roo.apply({
7272                       target: btnEl.id
7273                 }, this.tooltip));
7274             } else {
7275                 btnEl.dom[this.tooltipType] = this.tooltip;
7276             }
7277         }
7278         if(this.arrowTooltip){
7279             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7280         }
7281         if(this.hidden){
7282             this.hide();
7283         }
7284         if(this.disabled){
7285             this.disable();
7286         }
7287         if(this.pressed){
7288             this.el.addClass("x-btn-pressed");
7289         }
7290         if(Roo.isIE && !Roo.isIE7){
7291             this.autoWidth.defer(1, this);
7292         }else{
7293             this.autoWidth();
7294         }
7295         if(this.menu){
7296             this.menu.on("show", this.onMenuShow, this);
7297             this.menu.on("hide", this.onMenuHide, this);
7298         }
7299         this.fireEvent('render', this);
7300     },
7301
7302     // private
7303     autoWidth : function(){
7304         if(this.el){
7305             var tbl = this.el.child("table:first");
7306             var tbl2 = this.el.child("table:last");
7307             this.el.setWidth("auto");
7308             tbl.setWidth("auto");
7309             if(Roo.isIE7 && Roo.isStrict){
7310                 var ib = this.el.child('button:first');
7311                 if(ib && ib.getWidth() > 20){
7312                     ib.clip();
7313                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7314                 }
7315             }
7316             if(this.minWidth){
7317                 if(this.hidden){
7318                     this.el.beginMeasure();
7319                 }
7320                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7321                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7322                 }
7323                 if(this.hidden){
7324                     this.el.endMeasure();
7325                 }
7326             }
7327             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7328         } 
7329     },
7330     /**
7331      * Sets this button's click handler
7332      * @param {Function} handler The function to call when the button is clicked
7333      * @param {Object} scope (optional) Scope for the function passed above
7334      */
7335     setHandler : function(handler, scope){
7336         this.handler = handler;
7337         this.scope = scope;  
7338     },
7339     
7340     /**
7341      * Sets this button's arrow click handler
7342      * @param {Function} handler The function to call when the arrow is clicked
7343      * @param {Object} scope (optional) Scope for the function passed above
7344      */
7345     setArrowHandler : function(handler, scope){
7346         this.arrowHandler = handler;
7347         this.scope = scope;  
7348     },
7349     
7350     /**
7351      * Focus the button
7352      */
7353     focus : function(){
7354         if(this.el){
7355             this.el.child("button:first").focus();
7356         }
7357     },
7358
7359     // private
7360     onClick : function(e){
7361         e.preventDefault();
7362         if(!this.disabled){
7363             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7364                 if(this.menu && !this.menu.isVisible()){
7365                     this.menu.show(this.el, this.menuAlign);
7366                 }
7367                 this.fireEvent("arrowclick", this, e);
7368                 if(this.arrowHandler){
7369                     this.arrowHandler.call(this.scope || this, this, e);
7370                 }
7371             }else{
7372                 this.fireEvent("click", this, e);
7373                 if(this.handler){
7374                     this.handler.call(this.scope || this, this, e);
7375                 }
7376             }
7377         }
7378     },
7379     // private
7380     onMouseDown : function(e){
7381         if(!this.disabled){
7382             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7383         }
7384     },
7385     // private
7386     onMouseUp : function(e){
7387         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7388     }   
7389 });
7390
7391
7392 // backwards compat
7393 Roo.MenuButton = Roo.SplitButton;/*
7394  * Based on:
7395  * Ext JS Library 1.1.1
7396  * Copyright(c) 2006-2007, Ext JS, LLC.
7397  *
7398  * Originally Released Under LGPL - original licence link has changed is not relivant.
7399  *
7400  * Fork - LGPL
7401  * <script type="text/javascript">
7402  */
7403
7404 /**
7405  * @class Roo.Toolbar
7406  * Basic Toolbar class.
7407  * @constructor
7408  * Creates a new Toolbar
7409  * @param {Object} container The config object
7410  */ 
7411 Roo.Toolbar = function(container, buttons, config)
7412 {
7413     /// old consturctor format still supported..
7414     if(container instanceof Array){ // omit the container for later rendering
7415         buttons = container;
7416         config = buttons;
7417         container = null;
7418     }
7419     if (typeof(container) == 'object' && container.xtype) {
7420         config = container;
7421         container = config.container;
7422         buttons = config.buttons || []; // not really - use items!!
7423     }
7424     var xitems = [];
7425     if (config && config.items) {
7426         xitems = config.items;
7427         delete config.items;
7428     }
7429     Roo.apply(this, config);
7430     this.buttons = buttons;
7431     
7432     if(container){
7433         this.render(container);
7434     }
7435     this.xitems = xitems;
7436     Roo.each(xitems, function(b) {
7437         this.add(b);
7438     }, this);
7439     
7440 };
7441
7442 Roo.Toolbar.prototype = {
7443     /**
7444      * @cfg {Array} items
7445      * array of button configs or elements to add (will be converted to a MixedCollection)
7446      */
7447     
7448     /**
7449      * @cfg {String/HTMLElement/Element} container
7450      * The id or element that will contain the toolbar
7451      */
7452     // private
7453     render : function(ct){
7454         this.el = Roo.get(ct);
7455         if(this.cls){
7456             this.el.addClass(this.cls);
7457         }
7458         // using a table allows for vertical alignment
7459         // 100% width is needed by Safari...
7460         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7461         this.tr = this.el.child("tr", true);
7462         var autoId = 0;
7463         this.items = new Roo.util.MixedCollection(false, function(o){
7464             return o.id || ("item" + (++autoId));
7465         });
7466         if(this.buttons){
7467             this.add.apply(this, this.buttons);
7468             delete this.buttons;
7469         }
7470     },
7471
7472     /**
7473      * Adds element(s) to the toolbar -- this function takes a variable number of 
7474      * arguments of mixed type and adds them to the toolbar.
7475      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7476      * <ul>
7477      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7478      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7479      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7480      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7481      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7482      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7483      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7484      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7485      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7486      * </ul>
7487      * @param {Mixed} arg2
7488      * @param {Mixed} etc.
7489      */
7490     add : function(){
7491         var a = arguments, l = a.length;
7492         for(var i = 0; i < l; i++){
7493             this._add(a[i]);
7494         }
7495     },
7496     // private..
7497     _add : function(el) {
7498         
7499         if (el.xtype) {
7500             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7501         }
7502         
7503         if (el.applyTo){ // some kind of form field
7504             return this.addField(el);
7505         } 
7506         if (el.render){ // some kind of Toolbar.Item
7507             return this.addItem(el);
7508         }
7509         if (typeof el == "string"){ // string
7510             if(el == "separator" || el == "-"){
7511                 return this.addSeparator();
7512             }
7513             if (el == " "){
7514                 return this.addSpacer();
7515             }
7516             if(el == "->"){
7517                 return this.addFill();
7518             }
7519             return this.addText(el);
7520             
7521         }
7522         if(el.tagName){ // element
7523             return this.addElement(el);
7524         }
7525         if(typeof el == "object"){ // must be button config?
7526             return this.addButton(el);
7527         }
7528         // and now what?!?!
7529         return false;
7530         
7531     },
7532     
7533     /**
7534      * Add an Xtype element
7535      * @param {Object} xtype Xtype Object
7536      * @return {Object} created Object
7537      */
7538     addxtype : function(e){
7539         return this.add(e);  
7540     },
7541     
7542     /**
7543      * Returns the Element for this toolbar.
7544      * @return {Roo.Element}
7545      */
7546     getEl : function(){
7547         return this.el;  
7548     },
7549     
7550     /**
7551      * Adds a separator
7552      * @return {Roo.Toolbar.Item} The separator item
7553      */
7554     addSeparator : function(){
7555         return this.addItem(new Roo.Toolbar.Separator());
7556     },
7557
7558     /**
7559      * Adds a spacer element
7560      * @return {Roo.Toolbar.Spacer} The spacer item
7561      */
7562     addSpacer : function(){
7563         return this.addItem(new Roo.Toolbar.Spacer());
7564     },
7565
7566     /**
7567      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7568      * @return {Roo.Toolbar.Fill} The fill item
7569      */
7570     addFill : function(){
7571         return this.addItem(new Roo.Toolbar.Fill());
7572     },
7573
7574     /**
7575      * Adds any standard HTML element to the toolbar
7576      * @param {String/HTMLElement/Element} el The element or id of the element to add
7577      * @return {Roo.Toolbar.Item} The element's item
7578      */
7579     addElement : function(el){
7580         return this.addItem(new Roo.Toolbar.Item(el));
7581     },
7582     /**
7583      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7584      * @type Roo.util.MixedCollection  
7585      */
7586     items : false,
7587      
7588     /**
7589      * Adds any Toolbar.Item or subclass
7590      * @param {Roo.Toolbar.Item} item
7591      * @return {Roo.Toolbar.Item} The item
7592      */
7593     addItem : function(item){
7594         var td = this.nextBlock();
7595         item.render(td);
7596         this.items.add(item);
7597         return item;
7598     },
7599     
7600     /**
7601      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7602      * @param {Object/Array} config A button config or array of configs
7603      * @return {Roo.Toolbar.Button/Array}
7604      */
7605     addButton : function(config){
7606         if(config instanceof Array){
7607             var buttons = [];
7608             for(var i = 0, len = config.length; i < len; i++) {
7609                 buttons.push(this.addButton(config[i]));
7610             }
7611             return buttons;
7612         }
7613         var b = config;
7614         if(!(config instanceof Roo.Toolbar.Button)){
7615             b = config.split ?
7616                 new Roo.Toolbar.SplitButton(config) :
7617                 new Roo.Toolbar.Button(config);
7618         }
7619         var td = this.nextBlock();
7620         b.render(td);
7621         this.items.add(b);
7622         return b;
7623     },
7624     
7625     /**
7626      * Adds text to the toolbar
7627      * @param {String} text The text to add
7628      * @return {Roo.Toolbar.Item} The element's item
7629      */
7630     addText : function(text){
7631         return this.addItem(new Roo.Toolbar.TextItem(text));
7632     },
7633     
7634     /**
7635      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7636      * @param {Number} index The index where the item is to be inserted
7637      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7638      * @return {Roo.Toolbar.Button/Item}
7639      */
7640     insertButton : function(index, item){
7641         if(item instanceof Array){
7642             var buttons = [];
7643             for(var i = 0, len = item.length; i < len; i++) {
7644                buttons.push(this.insertButton(index + i, item[i]));
7645             }
7646             return buttons;
7647         }
7648         if (!(item instanceof Roo.Toolbar.Button)){
7649            item = new Roo.Toolbar.Button(item);
7650         }
7651         var td = document.createElement("td");
7652         this.tr.insertBefore(td, this.tr.childNodes[index]);
7653         item.render(td);
7654         this.items.insert(index, item);
7655         return item;
7656     },
7657     
7658     /**
7659      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7660      * @param {Object} config
7661      * @return {Roo.Toolbar.Item} The element's item
7662      */
7663     addDom : function(config, returnEl){
7664         var td = this.nextBlock();
7665         Roo.DomHelper.overwrite(td, config);
7666         var ti = new Roo.Toolbar.Item(td.firstChild);
7667         ti.render(td);
7668         this.items.add(ti);
7669         return ti;
7670     },
7671
7672     /**
7673      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7674      * @type Roo.util.MixedCollection  
7675      */
7676     fields : false,
7677     
7678     /**
7679      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7680      * Note: the field should not have been rendered yet. For a field that has already been
7681      * rendered, use {@link #addElement}.
7682      * @param {Roo.form.Field} field
7683      * @return {Roo.ToolbarItem}
7684      */
7685      
7686       
7687     addField : function(field) {
7688         if (!this.fields) {
7689             var autoId = 0;
7690             this.fields = new Roo.util.MixedCollection(false, function(o){
7691                 return o.id || ("item" + (++autoId));
7692             });
7693
7694         }
7695         
7696         var td = this.nextBlock();
7697         field.render(td);
7698         var ti = new Roo.Toolbar.Item(td.firstChild);
7699         ti.render(td);
7700         this.items.add(ti);
7701         this.fields.add(field);
7702         return ti;
7703     },
7704     /**
7705      * Hide the toolbar
7706      * @method hide
7707      */
7708      
7709       
7710     hide : function()
7711     {
7712         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7713         this.el.child('div').hide();
7714     },
7715     /**
7716      * Show the toolbar
7717      * @method show
7718      */
7719     show : function()
7720     {
7721         this.el.child('div').show();
7722     },
7723       
7724     // private
7725     nextBlock : function(){
7726         var td = document.createElement("td");
7727         this.tr.appendChild(td);
7728         return td;
7729     },
7730
7731     // private
7732     destroy : function(){
7733         if(this.items){ // rendered?
7734             Roo.destroy.apply(Roo, this.items.items);
7735         }
7736         if(this.fields){ // rendered?
7737             Roo.destroy.apply(Roo, this.fields.items);
7738         }
7739         Roo.Element.uncache(this.el, this.tr);
7740     }
7741 };
7742
7743 /**
7744  * @class Roo.Toolbar.Item
7745  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7746  * @constructor
7747  * Creates a new Item
7748  * @param {HTMLElement} el 
7749  */
7750 Roo.Toolbar.Item = function(el){
7751     var cfg = {};
7752     if (typeof (el.xtype) != 'undefined') {
7753         cfg = el;
7754         el = cfg.el;
7755     }
7756     
7757     this.el = Roo.getDom(el);
7758     this.id = Roo.id(this.el);
7759     this.hidden = false;
7760     
7761     this.addEvents({
7762          /**
7763              * @event render
7764              * Fires when the button is rendered
7765              * @param {Button} this
7766              */
7767         'render': true
7768     });
7769     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7770 };
7771 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7772 //Roo.Toolbar.Item.prototype = {
7773     
7774     /**
7775      * Get this item's HTML Element
7776      * @return {HTMLElement}
7777      */
7778     getEl : function(){
7779        return this.el;  
7780     },
7781
7782     // private
7783     render : function(td){
7784         
7785          this.td = td;
7786         td.appendChild(this.el);
7787         
7788         this.fireEvent('render', this);
7789     },
7790     
7791     /**
7792      * Removes and destroys this item.
7793      */
7794     destroy : function(){
7795         this.td.parentNode.removeChild(this.td);
7796     },
7797     
7798     /**
7799      * Shows this item.
7800      */
7801     show: function(){
7802         this.hidden = false;
7803         this.td.style.display = "";
7804     },
7805     
7806     /**
7807      * Hides this item.
7808      */
7809     hide: function(){
7810         this.hidden = true;
7811         this.td.style.display = "none";
7812     },
7813     
7814     /**
7815      * Convenience function for boolean show/hide.
7816      * @param {Boolean} visible true to show/false to hide
7817      */
7818     setVisible: function(visible){
7819         if(visible) {
7820             this.show();
7821         }else{
7822             this.hide();
7823         }
7824     },
7825     
7826     /**
7827      * Try to focus this item.
7828      */
7829     focus : function(){
7830         Roo.fly(this.el).focus();
7831     },
7832     
7833     /**
7834      * Disables this item.
7835      */
7836     disable : function(){
7837         Roo.fly(this.td).addClass("x-item-disabled");
7838         this.disabled = true;
7839         this.el.disabled = true;
7840     },
7841     
7842     /**
7843      * Enables this item.
7844      */
7845     enable : function(){
7846         Roo.fly(this.td).removeClass("x-item-disabled");
7847         this.disabled = false;
7848         this.el.disabled = false;
7849     }
7850 });
7851
7852
7853 /**
7854  * @class Roo.Toolbar.Separator
7855  * @extends Roo.Toolbar.Item
7856  * A simple toolbar separator class
7857  * @constructor
7858  * Creates a new Separator
7859  */
7860 Roo.Toolbar.Separator = function(cfg){
7861     
7862     var s = document.createElement("span");
7863     s.className = "ytb-sep";
7864     if (cfg) {
7865         cfg.el = s;
7866     }
7867     
7868     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7869 };
7870 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7871     enable:Roo.emptyFn,
7872     disable:Roo.emptyFn,
7873     focus:Roo.emptyFn
7874 });
7875
7876 /**
7877  * @class Roo.Toolbar.Spacer
7878  * @extends Roo.Toolbar.Item
7879  * A simple element that adds extra horizontal space to a toolbar.
7880  * @constructor
7881  * Creates a new Spacer
7882  */
7883 Roo.Toolbar.Spacer = function(cfg){
7884     var s = document.createElement("div");
7885     s.className = "ytb-spacer";
7886     if (cfg) {
7887         cfg.el = s;
7888     }
7889     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7890 };
7891 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7892     enable:Roo.emptyFn,
7893     disable:Roo.emptyFn,
7894     focus:Roo.emptyFn
7895 });
7896
7897 /**
7898  * @class Roo.Toolbar.Fill
7899  * @extends Roo.Toolbar.Spacer
7900  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7901  * @constructor
7902  * Creates a new Spacer
7903  */
7904 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7905     // private
7906     render : function(td){
7907         td.style.width = '100%';
7908         Roo.Toolbar.Fill.superclass.render.call(this, td);
7909     }
7910 });
7911
7912 /**
7913  * @class Roo.Toolbar.TextItem
7914  * @extends Roo.Toolbar.Item
7915  * A simple class that renders text directly into a toolbar.
7916  * @constructor
7917  * Creates a new TextItem
7918  * @param {String} text
7919  */
7920 Roo.Toolbar.TextItem = function(cfg){
7921     var  text = cfg || "";
7922     if (typeof(cfg) == 'object') {
7923         text = cfg.text || "";
7924     }  else {
7925         cfg = null;
7926     }
7927     var s = document.createElement("span");
7928     s.className = "ytb-text";
7929     s.innerHTML = text;
7930     if (cfg) {
7931         cfg.el  = s;
7932     }
7933     
7934     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7935 };
7936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7937     
7938      
7939     enable:Roo.emptyFn,
7940     disable:Roo.emptyFn,
7941     focus:Roo.emptyFn
7942 });
7943
7944 /**
7945  * @class Roo.Toolbar.Button
7946  * @extends Roo.Button
7947  * A button that renders into a toolbar.
7948  * @constructor
7949  * Creates a new Button
7950  * @param {Object} config A standard {@link Roo.Button} config object
7951  */
7952 Roo.Toolbar.Button = function(config){
7953     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7954 };
7955 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7956     render : function(td){
7957         this.td = td;
7958         Roo.Toolbar.Button.superclass.render.call(this, td);
7959     },
7960     
7961     /**
7962      * Removes and destroys this button
7963      */
7964     destroy : function(){
7965         Roo.Toolbar.Button.superclass.destroy.call(this);
7966         this.td.parentNode.removeChild(this.td);
7967     },
7968     
7969     /**
7970      * Shows this button
7971      */
7972     show: function(){
7973         this.hidden = false;
7974         this.td.style.display = "";
7975     },
7976     
7977     /**
7978      * Hides this button
7979      */
7980     hide: function(){
7981         this.hidden = true;
7982         this.td.style.display = "none";
7983     },
7984
7985     /**
7986      * Disables this item
7987      */
7988     disable : function(){
7989         Roo.fly(this.td).addClass("x-item-disabled");
7990         this.disabled = true;
7991     },
7992
7993     /**
7994      * Enables this item
7995      */
7996     enable : function(){
7997         Roo.fly(this.td).removeClass("x-item-disabled");
7998         this.disabled = false;
7999     }
8000 });
8001 // backwards compat
8002 Roo.ToolbarButton = Roo.Toolbar.Button;
8003
8004 /**
8005  * @class Roo.Toolbar.SplitButton
8006  * @extends Roo.SplitButton
8007  * A menu button that renders into a toolbar.
8008  * @constructor
8009  * Creates a new SplitButton
8010  * @param {Object} config A standard {@link Roo.SplitButton} config object
8011  */
8012 Roo.Toolbar.SplitButton = function(config){
8013     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8014 };
8015 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8016     render : function(td){
8017         this.td = td;
8018         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8019     },
8020     
8021     /**
8022      * Removes and destroys this button
8023      */
8024     destroy : function(){
8025         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8026         this.td.parentNode.removeChild(this.td);
8027     },
8028     
8029     /**
8030      * Shows this button
8031      */
8032     show: function(){
8033         this.hidden = false;
8034         this.td.style.display = "";
8035     },
8036     
8037     /**
8038      * Hides this button
8039      */
8040     hide: function(){
8041         this.hidden = true;
8042         this.td.style.display = "none";
8043     }
8044 });
8045
8046 // backwards compat
8047 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8048  * Based on:
8049  * Ext JS Library 1.1.1
8050  * Copyright(c) 2006-2007, Ext JS, LLC.
8051  *
8052  * Originally Released Under LGPL - original licence link has changed is not relivant.
8053  *
8054  * Fork - LGPL
8055  * <script type="text/javascript">
8056  */
8057  
8058 /**
8059  * @class Roo.PagingToolbar
8060  * @extends Roo.Toolbar
8061  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8062  * @constructor
8063  * Create a new PagingToolbar
8064  * @param {Object} config The config object
8065  */
8066 Roo.PagingToolbar = function(el, ds, config)
8067 {
8068     // old args format still supported... - xtype is prefered..
8069     if (typeof(el) == 'object' && el.xtype) {
8070         // created from xtype...
8071         config = el;
8072         ds = el.dataSource;
8073         el = config.container;
8074     }
8075     var items = [];
8076     if (config.items) {
8077         items = config.items;
8078         config.items = [];
8079     }
8080     
8081     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8082     this.ds = ds;
8083     this.cursor = 0;
8084     this.renderButtons(this.el);
8085     this.bind(ds);
8086     
8087     // supprot items array.
8088    
8089     Roo.each(items, function(e) {
8090         this.add(Roo.factory(e));
8091     },this);
8092     
8093 };
8094
8095 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8096     /**
8097      * @cfg {Roo.data.Store} dataSource
8098      * The underlying data store providing the paged data
8099      */
8100     /**
8101      * @cfg {String/HTMLElement/Element} container
8102      * container The id or element that will contain the toolbar
8103      */
8104     /**
8105      * @cfg {Boolean} displayInfo
8106      * True to display the displayMsg (defaults to false)
8107      */
8108     /**
8109      * @cfg {Number} pageSize
8110      * The number of records to display per page (defaults to 20)
8111      */
8112     pageSize: 20,
8113     /**
8114      * @cfg {String} displayMsg
8115      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8116      */
8117     displayMsg : 'Displaying {0} - {1} of {2}',
8118     /**
8119      * @cfg {String} emptyMsg
8120      * The message to display when no records are found (defaults to "No data to display")
8121      */
8122     emptyMsg : 'No data to display',
8123     /**
8124      * Customizable piece of the default paging text (defaults to "Page")
8125      * @type String
8126      */
8127     beforePageText : "Page",
8128     /**
8129      * Customizable piece of the default paging text (defaults to "of %0")
8130      * @type String
8131      */
8132     afterPageText : "of {0}",
8133     /**
8134      * Customizable piece of the default paging text (defaults to "First Page")
8135      * @type String
8136      */
8137     firstText : "First Page",
8138     /**
8139      * Customizable piece of the default paging text (defaults to "Previous Page")
8140      * @type String
8141      */
8142     prevText : "Previous Page",
8143     /**
8144      * Customizable piece of the default paging text (defaults to "Next Page")
8145      * @type String
8146      */
8147     nextText : "Next Page",
8148     /**
8149      * Customizable piece of the default paging text (defaults to "Last Page")
8150      * @type String
8151      */
8152     lastText : "Last Page",
8153     /**
8154      * Customizable piece of the default paging text (defaults to "Refresh")
8155      * @type String
8156      */
8157     refreshText : "Refresh",
8158
8159     // private
8160     renderButtons : function(el){
8161         Roo.PagingToolbar.superclass.render.call(this, el);
8162         this.first = this.addButton({
8163             tooltip: this.firstText,
8164             cls: "x-btn-icon x-grid-page-first",
8165             disabled: true,
8166             handler: this.onClick.createDelegate(this, ["first"])
8167         });
8168         this.prev = this.addButton({
8169             tooltip: this.prevText,
8170             cls: "x-btn-icon x-grid-page-prev",
8171             disabled: true,
8172             handler: this.onClick.createDelegate(this, ["prev"])
8173         });
8174         //this.addSeparator();
8175         this.add(this.beforePageText);
8176         this.field = Roo.get(this.addDom({
8177            tag: "input",
8178            type: "text",
8179            size: "3",
8180            value: "1",
8181            cls: "x-grid-page-number"
8182         }).el);
8183         this.field.on("keydown", this.onPagingKeydown, this);
8184         this.field.on("focus", function(){this.dom.select();});
8185         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8186         this.field.setHeight(18);
8187         //this.addSeparator();
8188         this.next = this.addButton({
8189             tooltip: this.nextText,
8190             cls: "x-btn-icon x-grid-page-next",
8191             disabled: true,
8192             handler: this.onClick.createDelegate(this, ["next"])
8193         });
8194         this.last = this.addButton({
8195             tooltip: this.lastText,
8196             cls: "x-btn-icon x-grid-page-last",
8197             disabled: true,
8198             handler: this.onClick.createDelegate(this, ["last"])
8199         });
8200         //this.addSeparator();
8201         this.loading = this.addButton({
8202             tooltip: this.refreshText,
8203             cls: "x-btn-icon x-grid-loading",
8204             handler: this.onClick.createDelegate(this, ["refresh"])
8205         });
8206
8207         if(this.displayInfo){
8208             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8209         }
8210     },
8211
8212     // private
8213     updateInfo : function(){
8214         if(this.displayEl){
8215             var count = this.ds.getCount();
8216             var msg = count == 0 ?
8217                 this.emptyMsg :
8218                 String.format(
8219                     this.displayMsg,
8220                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8221                 );
8222             this.displayEl.update(msg);
8223         }
8224     },
8225
8226     // private
8227     onLoad : function(ds, r, o){
8228        this.cursor = o.params ? o.params.start : 0;
8229        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8230
8231        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8232        this.field.dom.value = ap;
8233        this.first.setDisabled(ap == 1);
8234        this.prev.setDisabled(ap == 1);
8235        this.next.setDisabled(ap == ps);
8236        this.last.setDisabled(ap == ps);
8237        this.loading.enable();
8238        this.updateInfo();
8239     },
8240
8241     // private
8242     getPageData : function(){
8243         var total = this.ds.getTotalCount();
8244         return {
8245             total : total,
8246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8248         };
8249     },
8250
8251     // private
8252     onLoadError : function(){
8253         this.loading.enable();
8254     },
8255
8256     // private
8257     onPagingKeydown : function(e){
8258         var k = e.getKey();
8259         var d = this.getPageData();
8260         if(k == e.RETURN){
8261             var v = this.field.dom.value, pageNum;
8262             if(!v || isNaN(pageNum = parseInt(v, 10))){
8263                 this.field.dom.value = d.activePage;
8264                 return;
8265             }
8266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8268             e.stopEvent();
8269         }
8270         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
8271         {
8272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8273           this.field.dom.value = pageNum;
8274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8275           e.stopEvent();
8276         }
8277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8278         {
8279           var v = this.field.dom.value, pageNum; 
8280           var increment = (e.shiftKey) ? 10 : 1;
8281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8282             increment *= -1;
8283           }
8284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8285             this.field.dom.value = d.activePage;
8286             return;
8287           }
8288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8289           {
8290             this.field.dom.value = parseInt(v, 10) + increment;
8291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8293           }
8294           e.stopEvent();
8295         }
8296     },
8297
8298     // private
8299     beforeLoad : function(){
8300         if(this.loading){
8301             this.loading.disable();
8302         }
8303     },
8304
8305     // private
8306     onClick : function(which){
8307         var ds = this.ds;
8308         switch(which){
8309             case "first":
8310                 ds.load({params:{start: 0, limit: this.pageSize}});
8311             break;
8312             case "prev":
8313                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8314             break;
8315             case "next":
8316                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8317             break;
8318             case "last":
8319                 var total = ds.getTotalCount();
8320                 var extra = total % this.pageSize;
8321                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8322                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8323             break;
8324             case "refresh":
8325                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8326             break;
8327         }
8328     },
8329
8330     /**
8331      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8332      * @param {Roo.data.Store} store The data store to unbind
8333      */
8334     unbind : function(ds){
8335         ds.un("beforeload", this.beforeLoad, this);
8336         ds.un("load", this.onLoad, this);
8337         ds.un("loadexception", this.onLoadError, this);
8338         ds.un("remove", this.updateInfo, this);
8339         ds.un("add", this.updateInfo, this);
8340         this.ds = undefined;
8341     },
8342
8343     /**
8344      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8345      * @param {Roo.data.Store} store The data store to bind
8346      */
8347     bind : function(ds){
8348         ds.on("beforeload", this.beforeLoad, this);
8349         ds.on("load", this.onLoad, this);
8350         ds.on("loadexception", this.onLoadError, this);
8351         ds.on("remove", this.updateInfo, this);
8352         ds.on("add", this.updateInfo, this);
8353         this.ds = ds;
8354     }
8355 });/*
8356  * Based on:
8357  * Ext JS Library 1.1.1
8358  * Copyright(c) 2006-2007, Ext JS, LLC.
8359  *
8360  * Originally Released Under LGPL - original licence link has changed is not relivant.
8361  *
8362  * Fork - LGPL
8363  * <script type="text/javascript">
8364  */
8365
8366 /**
8367  * @class Roo.Resizable
8368  * @extends Roo.util.Observable
8369  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8370  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8371  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8372  * the element will be wrapped for you automatically.</p>
8373  * <p>Here is the list of valid resize handles:</p>
8374  * <pre>
8375 Value   Description
8376 ------  -------------------
8377  'n'     north
8378  's'     south
8379  'e'     east
8380  'w'     west
8381  'nw'    northwest
8382  'sw'    southwest
8383  'se'    southeast
8384  'ne'    northeast
8385  'hd'    horizontal drag
8386  'all'   all
8387 </pre>
8388  * <p>Here's an example showing the creation of a typical Resizable:</p>
8389  * <pre><code>
8390 var resizer = new Roo.Resizable("element-id", {
8391     handles: 'all',
8392     minWidth: 200,
8393     minHeight: 100,
8394     maxWidth: 500,
8395     maxHeight: 400,
8396     pinned: true
8397 });
8398 resizer.on("resize", myHandler);
8399 </code></pre>
8400  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8401  * resizer.east.setDisplayed(false);</p>
8402  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8403  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8404  * resize operation's new size (defaults to [0, 0])
8405  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8406  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8407  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8408  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8409  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8410  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8411  * @cfg {Number} width The width of the element in pixels (defaults to null)
8412  * @cfg {Number} height The height of the element in pixels (defaults to null)
8413  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8414  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8415  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8416  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8417  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8418  * in favor of the handles config option (defaults to false)
8419  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8420  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8421  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8422  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8423  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8424  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8425  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8426  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8427  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8428  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8429  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8430  * @constructor
8431  * Create a new resizable component
8432  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8433  * @param {Object} config configuration options
8434   */
8435 Roo.Resizable = function(el, config)
8436 {
8437     this.el = Roo.get(el);
8438
8439     if(config && config.wrap){
8440         config.resizeChild = this.el;
8441         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8442         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8443         this.el.setStyle("overflow", "hidden");
8444         this.el.setPositioning(config.resizeChild.getPositioning());
8445         config.resizeChild.clearPositioning();
8446         if(!config.width || !config.height){
8447             var csize = config.resizeChild.getSize();
8448             this.el.setSize(csize.width, csize.height);
8449         }
8450         if(config.pinned && !config.adjustments){
8451             config.adjustments = "auto";
8452         }
8453     }
8454
8455     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8456     this.proxy.unselectable();
8457     this.proxy.enableDisplayMode('block');
8458
8459     Roo.apply(this, config);
8460
8461     if(this.pinned){
8462         this.disableTrackOver = true;
8463         this.el.addClass("x-resizable-pinned");
8464     }
8465     // if the element isn't positioned, make it relative
8466     var position = this.el.getStyle("position");
8467     if(position != "absolute" && position != "fixed"){
8468         this.el.setStyle("position", "relative");
8469     }
8470     if(!this.handles){ // no handles passed, must be legacy style
8471         this.handles = 's,e,se';
8472         if(this.multiDirectional){
8473             this.handles += ',n,w';
8474         }
8475     }
8476     if(this.handles == "all"){
8477         this.handles = "n s e w ne nw se sw";
8478     }
8479     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8480     var ps = Roo.Resizable.positions;
8481     for(var i = 0, len = hs.length; i < len; i++){
8482         if(hs[i] && ps[hs[i]]){
8483             var pos = ps[hs[i]];
8484             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8485         }
8486     }
8487     // legacy
8488     this.corner = this.southeast;
8489     
8490     // updateBox = the box can move..
8491     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8492         this.updateBox = true;
8493     }
8494
8495     this.activeHandle = null;
8496
8497     if(this.resizeChild){
8498         if(typeof this.resizeChild == "boolean"){
8499             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8500         }else{
8501             this.resizeChild = Roo.get(this.resizeChild, true);
8502         }
8503     }
8504     
8505     if(this.adjustments == "auto"){
8506         var rc = this.resizeChild;
8507         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8508         if(rc && (hw || hn)){
8509             rc.position("relative");
8510             rc.setLeft(hw ? hw.el.getWidth() : 0);
8511             rc.setTop(hn ? hn.el.getHeight() : 0);
8512         }
8513         this.adjustments = [
8514             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8515             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8516         ];
8517     }
8518
8519     if(this.draggable){
8520         this.dd = this.dynamic ?
8521             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8522         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8523     }
8524
8525     // public events
8526     this.addEvents({
8527         /**
8528          * @event beforeresize
8529          * Fired before resize is allowed. Set enabled to false to cancel resize.
8530          * @param {Roo.Resizable} this
8531          * @param {Roo.EventObject} e The mousedown event
8532          */
8533         "beforeresize" : true,
8534         /**
8535          * @event resizing
8536          * Fired a resizing.
8537          * @param {Roo.Resizable} this
8538          * @param {Number} x The new x position
8539          * @param {Number} y The new y position
8540          * @param {Number} w The new w width
8541          * @param {Number} h The new h hight
8542          * @param {Roo.EventObject} e The mouseup event
8543          */
8544         "resizing" : true,
8545         /**
8546          * @event resize
8547          * Fired after a resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Number} width The new width
8550          * @param {Number} height The new height
8551          * @param {Roo.EventObject} e The mouseup event
8552          */
8553         "resize" : true
8554     });
8555
8556     if(this.width !== null && this.height !== null){
8557         this.resizeTo(this.width, this.height);
8558     }else{
8559         this.updateChildSize();
8560     }
8561     if(Roo.isIE){
8562         this.el.dom.style.zoom = 1;
8563     }
8564     Roo.Resizable.superclass.constructor.call(this);
8565 };
8566
8567 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8568         resizeChild : false,
8569         adjustments : [0, 0],
8570         minWidth : 5,
8571         minHeight : 5,
8572         maxWidth : 10000,
8573         maxHeight : 10000,
8574         enabled : true,
8575         animate : false,
8576         duration : .35,
8577         dynamic : false,
8578         handles : false,
8579         multiDirectional : false,
8580         disableTrackOver : false,
8581         easing : 'easeOutStrong',
8582         widthIncrement : 0,
8583         heightIncrement : 0,
8584         pinned : false,
8585         width : null,
8586         height : null,
8587         preserveRatio : false,
8588         transparent: false,
8589         minX: 0,
8590         minY: 0,
8591         draggable: false,
8592
8593         /**
8594          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8595          */
8596         constrainTo: undefined,
8597         /**
8598          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8599          */
8600         resizeRegion: undefined,
8601
8602
8603     /**
8604      * Perform a manual resize
8605      * @param {Number} width
8606      * @param {Number} height
8607      */
8608     resizeTo : function(width, height){
8609         this.el.setSize(width, height);
8610         this.updateChildSize();
8611         this.fireEvent("resize", this, width, height, null);
8612     },
8613
8614     // private
8615     startSizing : function(e, handle){
8616         this.fireEvent("beforeresize", this, e);
8617         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8618
8619             if(!this.overlay){
8620                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8621                 this.overlay.unselectable();
8622                 this.overlay.enableDisplayMode("block");
8623                 this.overlay.on("mousemove", this.onMouseMove, this);
8624                 this.overlay.on("mouseup", this.onMouseUp, this);
8625             }
8626             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8627
8628             this.resizing = true;
8629             this.startBox = this.el.getBox();
8630             this.startPoint = e.getXY();
8631             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8632                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8633
8634             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8635             this.overlay.show();
8636
8637             if(this.constrainTo) {
8638                 var ct = Roo.get(this.constrainTo);
8639                 this.resizeRegion = ct.getRegion().adjust(
8640                     ct.getFrameWidth('t'),
8641                     ct.getFrameWidth('l'),
8642                     -ct.getFrameWidth('b'),
8643                     -ct.getFrameWidth('r')
8644                 );
8645             }
8646
8647             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8648             this.proxy.show();
8649             this.proxy.setBox(this.startBox);
8650             if(!this.dynamic){
8651                 this.proxy.setStyle('visibility', 'visible');
8652             }
8653         }
8654     },
8655
8656     // private
8657     onMouseDown : function(handle, e){
8658         if(this.enabled){
8659             e.stopEvent();
8660             this.activeHandle = handle;
8661             this.startSizing(e, handle);
8662         }
8663     },
8664
8665     // private
8666     onMouseUp : function(e){
8667         var size = this.resizeElement();
8668         this.resizing = false;
8669         this.handleOut();
8670         this.overlay.hide();
8671         this.proxy.hide();
8672         this.fireEvent("resize", this, size.width, size.height, e);
8673     },
8674
8675     // private
8676     updateChildSize : function(){
8677         
8678         if(this.resizeChild){
8679             var el = this.el;
8680             var child = this.resizeChild;
8681             var adj = this.adjustments;
8682             if(el.dom.offsetWidth){
8683                 var b = el.getSize(true);
8684                 child.setSize(b.width+adj[0], b.height+adj[1]);
8685             }
8686             // Second call here for IE
8687             // The first call enables instant resizing and
8688             // the second call corrects scroll bars if they
8689             // exist
8690             if(Roo.isIE){
8691                 setTimeout(function(){
8692                     if(el.dom.offsetWidth){
8693                         var b = el.getSize(true);
8694                         child.setSize(b.width+adj[0], b.height+adj[1]);
8695                     }
8696                 }, 10);
8697             }
8698         }
8699     },
8700
8701     // private
8702     snap : function(value, inc, min){
8703         if(!inc || !value) {
8704             return value;
8705         }
8706         var newValue = value;
8707         var m = value % inc;
8708         if(m > 0){
8709             if(m > (inc/2)){
8710                 newValue = value + (inc-m);
8711             }else{
8712                 newValue = value - m;
8713             }
8714         }
8715         return Math.max(min, newValue);
8716     },
8717
8718     // private
8719     resizeElement : function(){
8720         var box = this.proxy.getBox();
8721         if(this.updateBox){
8722             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8723         }else{
8724             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8725         }
8726         this.updateChildSize();
8727         if(!this.dynamic){
8728             this.proxy.hide();
8729         }
8730         return box;
8731     },
8732
8733     // private
8734     constrain : function(v, diff, m, mx){
8735         if(v - diff < m){
8736             diff = v - m;
8737         }else if(v - diff > mx){
8738             diff = mx - v;
8739         }
8740         return diff;
8741     },
8742
8743     // private
8744     onMouseMove : function(e){
8745         
8746         if(this.enabled){
8747             try{// try catch so if something goes wrong the user doesn't get hung
8748
8749             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8750                 return;
8751             }
8752
8753             //var curXY = this.startPoint;
8754             var curSize = this.curSize || this.startBox;
8755             var x = this.startBox.x, y = this.startBox.y;
8756             var ox = x, oy = y;
8757             var w = curSize.width, h = curSize.height;
8758             var ow = w, oh = h;
8759             var mw = this.minWidth, mh = this.minHeight;
8760             var mxw = this.maxWidth, mxh = this.maxHeight;
8761             var wi = this.widthIncrement;
8762             var hi = this.heightIncrement;
8763
8764             var eventXY = e.getXY();
8765             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8766             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8767
8768             var pos = this.activeHandle.position;
8769
8770             switch(pos){
8771                 case "east":
8772                     w += diffX;
8773                     w = Math.min(Math.max(mw, w), mxw);
8774                     break;
8775              
8776                 case "south":
8777                     h += diffY;
8778                     h = Math.min(Math.max(mh, h), mxh);
8779                     break;
8780                 case "southeast":
8781                     w += diffX;
8782                     h += diffY;
8783                     w = Math.min(Math.max(mw, w), mxw);
8784                     h = Math.min(Math.max(mh, h), mxh);
8785                     break;
8786                 case "north":
8787                     diffY = this.constrain(h, diffY, mh, mxh);
8788                     y += diffY;
8789                     h -= diffY;
8790                     break;
8791                 case "hdrag":
8792                     
8793                     if (wi) {
8794                         var adiffX = Math.abs(diffX);
8795                         var sub = (adiffX % wi); // how much 
8796                         if (sub > (wi/2)) { // far enough to snap
8797                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8798                         } else {
8799                             // remove difference.. 
8800                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8801                         }
8802                     }
8803                     x += diffX;
8804                     x = Math.max(this.minX, x);
8805                     break;
8806                 case "west":
8807                     diffX = this.constrain(w, diffX, mw, mxw);
8808                     x += diffX;
8809                     w -= diffX;
8810                     break;
8811                 case "northeast":
8812                     w += diffX;
8813                     w = Math.min(Math.max(mw, w), mxw);
8814                     diffY = this.constrain(h, diffY, mh, mxh);
8815                     y += diffY;
8816                     h -= diffY;
8817                     break;
8818                 case "northwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     diffY = this.constrain(h, diffY, mh, mxh);
8821                     y += diffY;
8822                     h -= diffY;
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826                case "southwest":
8827                     diffX = this.constrain(w, diffX, mw, mxw);
8828                     h += diffY;
8829                     h = Math.min(Math.max(mh, h), mxh);
8830                     x += diffX;
8831                     w -= diffX;
8832                     break;
8833             }
8834
8835             var sw = this.snap(w, wi, mw);
8836             var sh = this.snap(h, hi, mh);
8837             if(sw != w || sh != h){
8838                 switch(pos){
8839                     case "northeast":
8840                         y -= sh - h;
8841                     break;
8842                     case "north":
8843                         y -= sh - h;
8844                         break;
8845                     case "southwest":
8846                         x -= sw - w;
8847                     break;
8848                     case "west":
8849                         x -= sw - w;
8850                         break;
8851                     case "northwest":
8852                         x -= sw - w;
8853                         y -= sh - h;
8854                     break;
8855                 }
8856                 w = sw;
8857                 h = sh;
8858             }
8859
8860             if(this.preserveRatio){
8861                 switch(pos){
8862                     case "southeast":
8863                     case "east":
8864                         h = oh * (w/ow);
8865                         h = Math.min(Math.max(mh, h), mxh);
8866                         w = ow * (h/oh);
8867                        break;
8868                     case "south":
8869                         w = ow * (h/oh);
8870                         w = Math.min(Math.max(mw, w), mxw);
8871                         h = oh * (w/ow);
8872                         break;
8873                     case "northeast":
8874                         w = ow * (h/oh);
8875                         w = Math.min(Math.max(mw, w), mxw);
8876                         h = oh * (w/ow);
8877                     break;
8878                     case "north":
8879                         var tw = w;
8880                         w = ow * (h/oh);
8881                         w = Math.min(Math.max(mw, w), mxw);
8882                         h = oh * (w/ow);
8883                         x += (tw - w) / 2;
8884                         break;
8885                     case "southwest":
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         var tw = w;
8889                         w = ow * (h/oh);
8890                         x += tw - w;
8891                         break;
8892                     case "west":
8893                         var th = h;
8894                         h = oh * (w/ow);
8895                         h = Math.min(Math.max(mh, h), mxh);
8896                         y += (th - h) / 2;
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         x += tw - w;
8900                        break;
8901                     case "northwest":
8902                         var tw = w;
8903                         var th = h;
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         w = ow * (h/oh);
8907                         y += th - h;
8908                         x += tw - w;
8909                        break;
8910
8911                 }
8912             }
8913             if (pos == 'hdrag') {
8914                 w = ow;
8915             }
8916             this.proxy.setBounds(x, y, w, h);
8917             if(this.dynamic){
8918                 this.resizeElement();
8919             }
8920             }catch(e){}
8921         }
8922         this.fireEvent("resizing", this, x, y, w, h, e);
8923     },
8924
8925     // private
8926     handleOver : function(){
8927         if(this.enabled){
8928             this.el.addClass("x-resizable-over");
8929         }
8930     },
8931
8932     // private
8933     handleOut : function(){
8934         if(!this.resizing){
8935             this.el.removeClass("x-resizable-over");
8936         }
8937     },
8938
8939     /**
8940      * Returns the element this component is bound to.
8941      * @return {Roo.Element}
8942      */
8943     getEl : function(){
8944         return this.el;
8945     },
8946
8947     /**
8948      * Returns the resizeChild element (or null).
8949      * @return {Roo.Element}
8950      */
8951     getResizeChild : function(){
8952         return this.resizeChild;
8953     },
8954     groupHandler : function()
8955     {
8956         
8957     },
8958     /**
8959      * Destroys this resizable. If the element was wrapped and
8960      * removeEl is not true then the element remains.
8961      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8962      */
8963     destroy : function(removeEl){
8964         this.proxy.remove();
8965         if(this.overlay){
8966             this.overlay.removeAllListeners();
8967             this.overlay.remove();
8968         }
8969         var ps = Roo.Resizable.positions;
8970         for(var k in ps){
8971             if(typeof ps[k] != "function" && this[ps[k]]){
8972                 var h = this[ps[k]];
8973                 h.el.removeAllListeners();
8974                 h.el.remove();
8975             }
8976         }
8977         if(removeEl){
8978             this.el.update("");
8979             this.el.remove();
8980         }
8981     }
8982 });
8983
8984 // private
8985 // hash to map config positions to true positions
8986 Roo.Resizable.positions = {
8987     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8988     hd: "hdrag"
8989 };
8990
8991 // private
8992 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8993     if(!this.tpl){
8994         // only initialize the template if resizable is used
8995         var tpl = Roo.DomHelper.createTemplate(
8996             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8997         );
8998         tpl.compile();
8999         Roo.Resizable.Handle.prototype.tpl = tpl;
9000     }
9001     this.position = pos;
9002     this.rz = rz;
9003     // show north drag fro topdra
9004     var handlepos = pos == 'hdrag' ? 'north' : pos;
9005     
9006     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9007     if (pos == 'hdrag') {
9008         this.el.setStyle('cursor', 'pointer');
9009     }
9010     this.el.unselectable();
9011     if(transparent){
9012         this.el.setOpacity(0);
9013     }
9014     this.el.on("mousedown", this.onMouseDown, this);
9015     if(!disableTrackOver){
9016         this.el.on("mouseover", this.onMouseOver, this);
9017         this.el.on("mouseout", this.onMouseOut, this);
9018     }
9019 };
9020
9021 // private
9022 Roo.Resizable.Handle.prototype = {
9023     afterResize : function(rz){
9024         Roo.log('after?');
9025         // do nothing
9026     },
9027     // private
9028     onMouseDown : function(e){
9029         this.rz.onMouseDown(this, e);
9030     },
9031     // private
9032     onMouseOver : function(e){
9033         this.rz.handleOver(this, e);
9034     },
9035     // private
9036     onMouseOut : function(e){
9037         this.rz.handleOut(this, e);
9038     }
9039 };/*
9040  * Based on:
9041  * Ext JS Library 1.1.1
9042  * Copyright(c) 2006-2007, Ext JS, LLC.
9043  *
9044  * Originally Released Under LGPL - original licence link has changed is not relivant.
9045  *
9046  * Fork - LGPL
9047  * <script type="text/javascript">
9048  */
9049
9050 /**
9051  * @class Roo.Editor
9052  * @extends Roo.Component
9053  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9054  * @constructor
9055  * Create a new Editor
9056  * @param {Roo.form.Field} field The Field object (or descendant)
9057  * @param {Object} config The config object
9058  */
9059 Roo.Editor = function(field, config){
9060     Roo.Editor.superclass.constructor.call(this, config);
9061     this.field = field;
9062     this.addEvents({
9063         /**
9064              * @event beforestartedit
9065              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9066              * false from the handler of this event.
9067              * @param {Editor} this
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The field value being set
9070              */
9071         "beforestartedit" : true,
9072         /**
9073              * @event startedit
9074              * Fires when this editor is displayed
9075              * @param {Roo.Element} boundEl The underlying element bound to this editor
9076              * @param {Mixed} value The starting field value
9077              */
9078         "startedit" : true,
9079         /**
9080              * @event beforecomplete
9081              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9082              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9083              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9084              * event will not fire since no edit actually occurred.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "beforecomplete" : true,
9090         /**
9091              * @event complete
9092              * Fires after editing is complete and any changed value has been written to the underlying field.
9093              * @param {Editor} this
9094              * @param {Mixed} value The current field value
9095              * @param {Mixed} startValue The original field value
9096              */
9097         "complete" : true,
9098         /**
9099          * @event specialkey
9100          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9101          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9102          * @param {Roo.form.Field} this
9103          * @param {Roo.EventObject} e The event object
9104          */
9105         "specialkey" : true
9106     });
9107 };
9108
9109 Roo.extend(Roo.Editor, Roo.Component, {
9110     /**
9111      * @cfg {Boolean/String} autosize
9112      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9113      * or "height" to adopt the height only (defaults to false)
9114      */
9115     /**
9116      * @cfg {Boolean} revertInvalid
9117      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9118      * validation fails (defaults to true)
9119      */
9120     /**
9121      * @cfg {Boolean} ignoreNoChange
9122      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9123      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9124      * will never be ignored.
9125      */
9126     /**
9127      * @cfg {Boolean} hideEl
9128      * False to keep the bound element visible while the editor is displayed (defaults to true)
9129      */
9130     /**
9131      * @cfg {Mixed} value
9132      * The data value of the underlying field (defaults to "")
9133      */
9134     value : "",
9135     /**
9136      * @cfg {String} alignment
9137      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9138      */
9139     alignment: "c-c?",
9140     /**
9141      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9142      * for bottom-right shadow (defaults to "frame")
9143      */
9144     shadow : "frame",
9145     /**
9146      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9147      */
9148     constrain : false,
9149     /**
9150      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9151      */
9152     completeOnEnter : false,
9153     /**
9154      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9155      */
9156     cancelOnEsc : false,
9157     /**
9158      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9159      */
9160     updateEl : false,
9161
9162     // private
9163     onRender : function(ct, position){
9164         this.el = new Roo.Layer({
9165             shadow: this.shadow,
9166             cls: "x-editor",
9167             parentEl : ct,
9168             shim : this.shim,
9169             shadowOffset:4,
9170             id: this.id,
9171             constrain: this.constrain
9172         });
9173         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9174         if(this.field.msgTarget != 'title'){
9175             this.field.msgTarget = 'qtip';
9176         }
9177         this.field.render(this.el);
9178         if(Roo.isGecko){
9179             this.field.el.dom.setAttribute('autocomplete', 'off');
9180         }
9181         this.field.on("specialkey", this.onSpecialKey, this);
9182         if(this.swallowKeys){
9183             this.field.el.swallowEvent(['keydown','keypress']);
9184         }
9185         this.field.show();
9186         this.field.on("blur", this.onBlur, this);
9187         if(this.field.grow){
9188             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9189         }
9190     },
9191
9192     onSpecialKey : function(field, e)
9193     {
9194         //Roo.log('editor onSpecialKey');
9195         if(this.completeOnEnter && e.getKey() == e.ENTER){
9196             e.stopEvent();
9197             this.completeEdit();
9198             return;
9199         }
9200         // do not fire special key otherwise it might hide close the editor...
9201         if(e.getKey() == e.ENTER){    
9202             return;
9203         }
9204         if(this.cancelOnEsc && e.getKey() == e.ESC){
9205             this.cancelEdit();
9206             return;
9207         } 
9208         this.fireEvent('specialkey', field, e);
9209     
9210     },
9211
9212     /**
9213      * Starts the editing process and shows the editor.
9214      * @param {String/HTMLElement/Element} el The element to edit
9215      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9216       * to the innerHTML of el.
9217      */
9218     startEdit : function(el, value){
9219         if(this.editing){
9220             this.completeEdit();
9221         }
9222         this.boundEl = Roo.get(el);
9223         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9224         if(!this.rendered){
9225             this.render(this.parentEl || document.body);
9226         }
9227         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9228             return;
9229         }
9230         this.startValue = v;
9231         this.field.setValue(v);
9232         if(this.autoSize){
9233             var sz = this.boundEl.getSize();
9234             switch(this.autoSize){
9235                 case "width":
9236                 this.setSize(sz.width,  "");
9237                 break;
9238                 case "height":
9239                 this.setSize("",  sz.height);
9240                 break;
9241                 default:
9242                 this.setSize(sz.width,  sz.height);
9243             }
9244         }
9245         this.el.alignTo(this.boundEl, this.alignment);
9246         this.editing = true;
9247         if(Roo.QuickTips){
9248             Roo.QuickTips.disable();
9249         }
9250         this.show();
9251     },
9252
9253     /**
9254      * Sets the height and width of this editor.
9255      * @param {Number} width The new width
9256      * @param {Number} height The new height
9257      */
9258     setSize : function(w, h){
9259         this.field.setSize(w, h);
9260         if(this.el){
9261             this.el.sync();
9262         }
9263     },
9264
9265     /**
9266      * Realigns the editor to the bound field based on the current alignment config value.
9267      */
9268     realign : function(){
9269         this.el.alignTo(this.boundEl, this.alignment);
9270     },
9271
9272     /**
9273      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9274      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9275      */
9276     completeEdit : function(remainVisible){
9277         if(!this.editing){
9278             return;
9279         }
9280         var v = this.getValue();
9281         if(this.revertInvalid !== false && !this.field.isValid()){
9282             v = this.startValue;
9283             this.cancelEdit(true);
9284         }
9285         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9286             this.editing = false;
9287             this.hide();
9288             return;
9289         }
9290         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9291             this.editing = false;
9292             if(this.updateEl && this.boundEl){
9293                 this.boundEl.update(v);
9294             }
9295             if(remainVisible !== true){
9296                 this.hide();
9297             }
9298             this.fireEvent("complete", this, v, this.startValue);
9299         }
9300     },
9301
9302     // private
9303     onShow : function(){
9304         this.el.show();
9305         if(this.hideEl !== false){
9306             this.boundEl.hide();
9307         }
9308         this.field.show();
9309         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9310             this.fixIEFocus = true;
9311             this.deferredFocus.defer(50, this);
9312         }else{
9313             this.field.focus();
9314         }
9315         this.fireEvent("startedit", this.boundEl, this.startValue);
9316     },
9317
9318     deferredFocus : function(){
9319         if(this.editing){
9320             this.field.focus();
9321         }
9322     },
9323
9324     /**
9325      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9326      * reverted to the original starting value.
9327      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9328      * cancel (defaults to false)
9329      */
9330     cancelEdit : function(remainVisible){
9331         if(this.editing){
9332             this.setValue(this.startValue);
9333             if(remainVisible !== true){
9334                 this.hide();
9335             }
9336         }
9337     },
9338
9339     // private
9340     onBlur : function(){
9341         if(this.allowBlur !== true && this.editing){
9342             this.completeEdit();
9343         }
9344     },
9345
9346     // private
9347     onHide : function(){
9348         if(this.editing){
9349             this.completeEdit();
9350             return;
9351         }
9352         this.field.blur();
9353         if(this.field.collapse){
9354             this.field.collapse();
9355         }
9356         this.el.hide();
9357         if(this.hideEl !== false){
9358             this.boundEl.show();
9359         }
9360         if(Roo.QuickTips){
9361             Roo.QuickTips.enable();
9362         }
9363     },
9364
9365     /**
9366      * Sets the data value of the editor
9367      * @param {Mixed} value Any valid value supported by the underlying field
9368      */
9369     setValue : function(v){
9370         this.field.setValue(v);
9371     },
9372
9373     /**
9374      * Gets the data value of the editor
9375      * @return {Mixed} The data value
9376      */
9377     getValue : function(){
9378         return this.field.getValue();
9379     }
9380 });/*
9381  * Based on:
9382  * Ext JS Library 1.1.1
9383  * Copyright(c) 2006-2007, Ext JS, LLC.
9384  *
9385  * Originally Released Under LGPL - original licence link has changed is not relivant.
9386  *
9387  * Fork - LGPL
9388  * <script type="text/javascript">
9389  */
9390  
9391 /**
9392  * @class Roo.BasicDialog
9393  * @extends Roo.util.Observable
9394  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9395  * <pre><code>
9396 var dlg = new Roo.BasicDialog("my-dlg", {
9397     height: 200,
9398     width: 300,
9399     minHeight: 100,
9400     minWidth: 150,
9401     modal: true,
9402     proxyDrag: true,
9403     shadow: true
9404 });
9405 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9406 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9407 dlg.addButton('Cancel', dlg.hide, dlg);
9408 dlg.show();
9409 </code></pre>
9410   <b>A Dialog should always be a direct child of the body element.</b>
9411  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9412  * @cfg {String} title Default text to display in the title bar (defaults to null)
9413  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9414  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9415  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9416  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9417  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9418  * (defaults to null with no animation)
9419  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9420  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9421  * property for valid values (defaults to 'all')
9422  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9423  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9424  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9425  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9426  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9427  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9428  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9429  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9430  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9431  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9432  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9433  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9434  * draggable = true (defaults to false)
9435  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9436  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9437  * shadow (defaults to false)
9438  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9439  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9440  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9441  * @cfg {Array} buttons Array of buttons
9442  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9443  * @constructor
9444  * Create a new BasicDialog.
9445  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9446  * @param {Object} config Configuration options
9447  */
9448 Roo.BasicDialog = function(el, config){
9449     this.el = Roo.get(el);
9450     var dh = Roo.DomHelper;
9451     if(!this.el && config && config.autoCreate){
9452         if(typeof config.autoCreate == "object"){
9453             if(!config.autoCreate.id){
9454                 config.autoCreate.id = el;
9455             }
9456             this.el = dh.append(document.body,
9457                         config.autoCreate, true);
9458         }else{
9459             this.el = dh.append(document.body,
9460                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9461         }
9462     }
9463     el = this.el;
9464     el.setDisplayed(true);
9465     el.hide = this.hideAction;
9466     this.id = el.id;
9467     el.addClass("x-dlg");
9468
9469     Roo.apply(this, config);
9470
9471     this.proxy = el.createProxy("x-dlg-proxy");
9472     this.proxy.hide = this.hideAction;
9473     this.proxy.setOpacity(.5);
9474     this.proxy.hide();
9475
9476     if(config.width){
9477         el.setWidth(config.width);
9478     }
9479     if(config.height){
9480         el.setHeight(config.height);
9481     }
9482     this.size = el.getSize();
9483     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9484         this.xy = [config.x,config.y];
9485     }else{
9486         this.xy = el.getCenterXY(true);
9487     }
9488     /** The header element @type Roo.Element */
9489     this.header = el.child("> .x-dlg-hd");
9490     /** The body element @type Roo.Element */
9491     this.body = el.child("> .x-dlg-bd");
9492     /** The footer element @type Roo.Element */
9493     this.footer = el.child("> .x-dlg-ft");
9494
9495     if(!this.header){
9496         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9497     }
9498     if(!this.body){
9499         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9500     }
9501
9502     this.header.unselectable();
9503     if(this.title){
9504         this.header.update(this.title);
9505     }
9506     // this element allows the dialog to be focused for keyboard event
9507     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9508     this.focusEl.swallowEvent("click", true);
9509
9510     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9511
9512     // wrap the body and footer for special rendering
9513     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9514     if(this.footer){
9515         this.bwrap.dom.appendChild(this.footer.dom);
9516     }
9517
9518     this.bg = this.el.createChild({
9519         tag: "div", cls:"x-dlg-bg",
9520         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9521     });
9522     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9523
9524
9525     if(this.autoScroll !== false && !this.autoTabs){
9526         this.body.setStyle("overflow", "auto");
9527     }
9528
9529     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9530
9531     if(this.closable !== false){
9532         this.el.addClass("x-dlg-closable");
9533         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9534         this.close.on("click", this.closeClick, this);
9535         this.close.addClassOnOver("x-dlg-close-over");
9536     }
9537     if(this.collapsible !== false){
9538         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9539         this.collapseBtn.on("click", this.collapseClick, this);
9540         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9541         this.header.on("dblclick", this.collapseClick, this);
9542     }
9543     if(this.resizable !== false){
9544         this.el.addClass("x-dlg-resizable");
9545         this.resizer = new Roo.Resizable(el, {
9546             minWidth: this.minWidth || 80,
9547             minHeight:this.minHeight || 80,
9548             handles: this.resizeHandles || "all",
9549             pinned: true
9550         });
9551         this.resizer.on("beforeresize", this.beforeResize, this);
9552         this.resizer.on("resize", this.onResize, this);
9553     }
9554     if(this.draggable !== false){
9555         el.addClass("x-dlg-draggable");
9556         if (!this.proxyDrag) {
9557             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9558         }
9559         else {
9560             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9561         }
9562         dd.setHandleElId(this.header.id);
9563         dd.endDrag = this.endMove.createDelegate(this);
9564         dd.startDrag = this.startMove.createDelegate(this);
9565         dd.onDrag = this.onDrag.createDelegate(this);
9566         dd.scroll = false;
9567         this.dd = dd;
9568     }
9569     if(this.modal){
9570         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9571         this.mask.enableDisplayMode("block");
9572         this.mask.hide();
9573         this.el.addClass("x-dlg-modal");
9574     }
9575     if(this.shadow){
9576         this.shadow = new Roo.Shadow({
9577             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9578             offset : this.shadowOffset
9579         });
9580     }else{
9581         this.shadowOffset = 0;
9582     }
9583     if(Roo.useShims && this.shim !== false){
9584         this.shim = this.el.createShim();
9585         this.shim.hide = this.hideAction;
9586         this.shim.hide();
9587     }else{
9588         this.shim = false;
9589     }
9590     if(this.autoTabs){
9591         this.initTabs();
9592     }
9593     if (this.buttons) { 
9594         var bts= this.buttons;
9595         this.buttons = [];
9596         Roo.each(bts, function(b) {
9597             this.addButton(b);
9598         }, this);
9599     }
9600     
9601     
9602     this.addEvents({
9603         /**
9604          * @event keydown
9605          * Fires when a key is pressed
9606          * @param {Roo.BasicDialog} this
9607          * @param {Roo.EventObject} e
9608          */
9609         "keydown" : true,
9610         /**
9611          * @event move
9612          * Fires when this dialog is moved by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} x The new page X
9615          * @param {Number} y The new page Y
9616          */
9617         "move" : true,
9618         /**
9619          * @event resize
9620          * Fires when this dialog is resized by the user.
9621          * @param {Roo.BasicDialog} this
9622          * @param {Number} width The new width
9623          * @param {Number} height The new height
9624          */
9625         "resize" : true,
9626         /**
9627          * @event beforehide
9628          * Fires before this dialog is hidden.
9629          * @param {Roo.BasicDialog} this
9630          */
9631         "beforehide" : true,
9632         /**
9633          * @event hide
9634          * Fires when this dialog is hidden.
9635          * @param {Roo.BasicDialog} this
9636          */
9637         "hide" : true,
9638         /**
9639          * @event beforeshow
9640          * Fires before this dialog is shown.
9641          * @param {Roo.BasicDialog} this
9642          */
9643         "beforeshow" : true,
9644         /**
9645          * @event show
9646          * Fires when this dialog is shown.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "show" : true
9650     });
9651     el.on("keydown", this.onKeyDown, this);
9652     el.on("mousedown", this.toFront, this);
9653     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9654     this.el.hide();
9655     Roo.DialogManager.register(this);
9656     Roo.BasicDialog.superclass.constructor.call(this);
9657 };
9658
9659 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9660     shadowOffset: Roo.isIE ? 6 : 5,
9661     minHeight: 80,
9662     minWidth: 200,
9663     minButtonWidth: 75,
9664     defaultButton: null,
9665     buttonAlign: "right",
9666     tabTag: 'div',
9667     firstShow: true,
9668
9669     /**
9670      * Sets the dialog title text
9671      * @param {String} text The title text to display
9672      * @return {Roo.BasicDialog} this
9673      */
9674     setTitle : function(text){
9675         this.header.update(text);
9676         return this;
9677     },
9678
9679     // private
9680     closeClick : function(){
9681         this.hide();
9682     },
9683
9684     // private
9685     collapseClick : function(){
9686         this[this.collapsed ? "expand" : "collapse"]();
9687     },
9688
9689     /**
9690      * Collapses the dialog to its minimized state (only the title bar is visible).
9691      * Equivalent to the user clicking the collapse dialog button.
9692      */
9693     collapse : function(){
9694         if(!this.collapsed){
9695             this.collapsed = true;
9696             this.el.addClass("x-dlg-collapsed");
9697             this.restoreHeight = this.el.getHeight();
9698             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9699         }
9700     },
9701
9702     /**
9703      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9704      * clicking the expand dialog button.
9705      */
9706     expand : function(){
9707         if(this.collapsed){
9708             this.collapsed = false;
9709             this.el.removeClass("x-dlg-collapsed");
9710             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9711         }
9712     },
9713
9714     /**
9715      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9716      * @return {Roo.TabPanel} The tabs component
9717      */
9718     initTabs : function(){
9719         var tabs = this.getTabs();
9720         while(tabs.getTab(0)){
9721             tabs.removeTab(0);
9722         }
9723         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9724             var dom = el.dom;
9725             tabs.addTab(Roo.id(dom), dom.title);
9726             dom.title = "";
9727         });
9728         tabs.activate(0);
9729         return tabs;
9730     },
9731
9732     // private
9733     beforeResize : function(){
9734         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9735     },
9736
9737     // private
9738     onResize : function(){
9739         this.refreshSize();
9740         this.syncBodyHeight();
9741         this.adjustAssets();
9742         this.focus();
9743         this.fireEvent("resize", this, this.size.width, this.size.height);
9744     },
9745
9746     // private
9747     onKeyDown : function(e){
9748         if(this.isVisible()){
9749             this.fireEvent("keydown", this, e);
9750         }
9751     },
9752
9753     /**
9754      * Resizes the dialog.
9755      * @param {Number} width
9756      * @param {Number} height
9757      * @return {Roo.BasicDialog} this
9758      */
9759     resizeTo : function(width, height){
9760         this.el.setSize(width, height);
9761         this.size = {width: width, height: height};
9762         this.syncBodyHeight();
9763         if(this.fixedcenter){
9764             this.center();
9765         }
9766         if(this.isVisible()){
9767             this.constrainXY();
9768             this.adjustAssets();
9769         }
9770         this.fireEvent("resize", this, width, height);
9771         return this;
9772     },
9773
9774
9775     /**
9776      * Resizes the dialog to fit the specified content size.
9777      * @param {Number} width
9778      * @param {Number} height
9779      * @return {Roo.BasicDialog} this
9780      */
9781     setContentSize : function(w, h){
9782         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9783         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9784         //if(!this.el.isBorderBox()){
9785             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9786             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9787         //}
9788         if(this.tabs){
9789             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9790             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9791         }
9792         this.resizeTo(w, h);
9793         return this;
9794     },
9795
9796     /**
9797      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9798      * executed in response to a particular key being pressed while the dialog is active.
9799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9800      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9801      * @param {Function} fn The function to call
9802      * @param {Object} scope (optional) The scope of the function
9803      * @return {Roo.BasicDialog} this
9804      */
9805     addKeyListener : function(key, fn, scope){
9806         var keyCode, shift, ctrl, alt;
9807         if(typeof key == "object" && !(key instanceof Array)){
9808             keyCode = key["key"];
9809             shift = key["shift"];
9810             ctrl = key["ctrl"];
9811             alt = key["alt"];
9812         }else{
9813             keyCode = key;
9814         }
9815         var handler = function(dlg, e){
9816             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9817                 var k = e.getKey();
9818                 if(keyCode instanceof Array){
9819                     for(var i = 0, len = keyCode.length; i < len; i++){
9820                         if(keyCode[i] == k){
9821                           fn.call(scope || window, dlg, k, e);
9822                           return;
9823                         }
9824                     }
9825                 }else{
9826                     if(k == keyCode){
9827                         fn.call(scope || window, dlg, k, e);
9828                     }
9829                 }
9830             }
9831         };
9832         this.on("keydown", handler);
9833         return this;
9834     },
9835
9836     /**
9837      * Returns the TabPanel component (creates it if it doesn't exist).
9838      * Note: If you wish to simply check for the existence of tabs without creating them,
9839      * check for a null 'tabs' property.
9840      * @return {Roo.TabPanel} The tabs component
9841      */
9842     getTabs : function(){
9843         if(!this.tabs){
9844             this.el.addClass("x-dlg-auto-tabs");
9845             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9846             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9847         }
9848         return this.tabs;
9849     },
9850
9851     /**
9852      * Adds a button to the footer section of the dialog.
9853      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9854      * object or a valid Roo.DomHelper element config
9855      * @param {Function} handler The function called when the button is clicked
9856      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9857      * @return {Roo.Button} The new button
9858      */
9859     addButton : function(config, handler, scope){
9860         var dh = Roo.DomHelper;
9861         if(!this.footer){
9862             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9863         }
9864         if(!this.btnContainer){
9865             var tb = this.footer.createChild({
9866
9867                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9868                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9869             }, null, true);
9870             this.btnContainer = tb.firstChild.firstChild.firstChild;
9871         }
9872         var bconfig = {
9873             handler: handler,
9874             scope: scope,
9875             minWidth: this.minButtonWidth,
9876             hideParent:true
9877         };
9878         if(typeof config == "string"){
9879             bconfig.text = config;
9880         }else{
9881             if(config.tag){
9882                 bconfig.dhconfig = config;
9883             }else{
9884                 Roo.apply(bconfig, config);
9885             }
9886         }
9887         var fc = false;
9888         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9889             bconfig.position = Math.max(0, bconfig.position);
9890             fc = this.btnContainer.childNodes[bconfig.position];
9891         }
9892          
9893         var btn = new Roo.Button(
9894             fc ? 
9895                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9896                 : this.btnContainer.appendChild(document.createElement("td")),
9897             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9898             bconfig
9899         );
9900         this.syncBodyHeight();
9901         if(!this.buttons){
9902             /**
9903              * Array of all the buttons that have been added to this dialog via addButton
9904              * @type Array
9905              */
9906             this.buttons = [];
9907         }
9908         this.buttons.push(btn);
9909         return btn;
9910     },
9911
9912     /**
9913      * Sets the default button to be focused when the dialog is displayed.
9914      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9915      * @return {Roo.BasicDialog} this
9916      */
9917     setDefaultButton : function(btn){
9918         this.defaultButton = btn;
9919         return this;
9920     },
9921
9922     // private
9923     getHeaderFooterHeight : function(safe){
9924         var height = 0;
9925         if(this.header){
9926            height += this.header.getHeight();
9927         }
9928         if(this.footer){
9929            var fm = this.footer.getMargins();
9930             height += (this.footer.getHeight()+fm.top+fm.bottom);
9931         }
9932         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9933         height += this.centerBg.getPadding("tb");
9934         return height;
9935     },
9936
9937     // private
9938     syncBodyHeight : function()
9939     {
9940         var bd = this.body, // the text
9941             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9942             bw = this.bwrap;
9943         var height = this.size.height - this.getHeaderFooterHeight(false);
9944         bd.setHeight(height-bd.getMargins("tb"));
9945         var hh = this.header.getHeight();
9946         var h = this.size.height-hh;
9947         cb.setHeight(h);
9948         
9949         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9950         bw.setHeight(h-cb.getPadding("tb"));
9951         
9952         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9953         bd.setWidth(bw.getWidth(true));
9954         if(this.tabs){
9955             this.tabs.syncHeight();
9956             if(Roo.isIE){
9957                 this.tabs.el.repaint();
9958             }
9959         }
9960     },
9961
9962     /**
9963      * Restores the previous state of the dialog if Roo.state is configured.
9964      * @return {Roo.BasicDialog} this
9965      */
9966     restoreState : function(){
9967         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9968         if(box && box.width){
9969             this.xy = [box.x, box.y];
9970             this.resizeTo(box.width, box.height);
9971         }
9972         return this;
9973     },
9974
9975     // private
9976     beforeShow : function(){
9977         this.expand();
9978         if(this.fixedcenter){
9979             this.xy = this.el.getCenterXY(true);
9980         }
9981         if(this.modal){
9982             Roo.get(document.body).addClass("x-body-masked");
9983             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9984             this.mask.show();
9985         }
9986         this.constrainXY();
9987     },
9988
9989     // private
9990     animShow : function(){
9991         var b = Roo.get(this.animateTarget).getBox();
9992         this.proxy.setSize(b.width, b.height);
9993         this.proxy.setLocation(b.x, b.y);
9994         this.proxy.show();
9995         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9996                     true, .35, this.showEl.createDelegate(this));
9997     },
9998
9999     /**
10000      * Shows the dialog.
10001      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10002      * @return {Roo.BasicDialog} this
10003      */
10004     show : function(animateTarget){
10005         if (this.fireEvent("beforeshow", this) === false){
10006             return;
10007         }
10008         if(this.syncHeightBeforeShow){
10009             this.syncBodyHeight();
10010         }else if(this.firstShow){
10011             this.firstShow = false;
10012             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10013         }
10014         this.animateTarget = animateTarget || this.animateTarget;
10015         if(!this.el.isVisible()){
10016             this.beforeShow();
10017             if(this.animateTarget && Roo.get(this.animateTarget)){
10018                 this.animShow();
10019             }else{
10020                 this.showEl();
10021             }
10022         }
10023         return this;
10024     },
10025
10026     // private
10027     showEl : function(){
10028         this.proxy.hide();
10029         this.el.setXY(this.xy);
10030         this.el.show();
10031         this.adjustAssets(true);
10032         this.toFront();
10033         this.focus();
10034         // IE peekaboo bug - fix found by Dave Fenwick
10035         if(Roo.isIE){
10036             this.el.repaint();
10037         }
10038         this.fireEvent("show", this);
10039     },
10040
10041     /**
10042      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10043      * dialog itself will receive focus.
10044      */
10045     focus : function(){
10046         if(this.defaultButton){
10047             this.defaultButton.focus();
10048         }else{
10049             this.focusEl.focus();
10050         }
10051     },
10052
10053     // private
10054     constrainXY : function(){
10055         if(this.constraintoviewport !== false){
10056             if(!this.viewSize){
10057                 if(this.container){
10058                     var s = this.container.getSize();
10059                     this.viewSize = [s.width, s.height];
10060                 }else{
10061                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10062                 }
10063             }
10064             var s = Roo.get(this.container||document).getScroll();
10065
10066             var x = this.xy[0], y = this.xy[1];
10067             var w = this.size.width, h = this.size.height;
10068             var vw = this.viewSize[0], vh = this.viewSize[1];
10069             // only move it if it needs it
10070             var moved = false;
10071             // first validate right/bottom
10072             if(x + w > vw+s.left){
10073                 x = vw - w;
10074                 moved = true;
10075             }
10076             if(y + h > vh+s.top){
10077                 y = vh - h;
10078                 moved = true;
10079             }
10080             // then make sure top/left isn't negative
10081             if(x < s.left){
10082                 x = s.left;
10083                 moved = true;
10084             }
10085             if(y < s.top){
10086                 y = s.top;
10087                 moved = true;
10088             }
10089             if(moved){
10090                 // cache xy
10091                 this.xy = [x, y];
10092                 if(this.isVisible()){
10093                     this.el.setLocation(x, y);
10094                     this.adjustAssets();
10095                 }
10096             }
10097         }
10098     },
10099
10100     // private
10101     onDrag : function(){
10102         if(!this.proxyDrag){
10103             this.xy = this.el.getXY();
10104             this.adjustAssets();
10105         }
10106     },
10107
10108     // private
10109     adjustAssets : function(doShow){
10110         var x = this.xy[0], y = this.xy[1];
10111         var w = this.size.width, h = this.size.height;
10112         if(doShow === true){
10113             if(this.shadow){
10114                 this.shadow.show(this.el);
10115             }
10116             if(this.shim){
10117                 this.shim.show();
10118             }
10119         }
10120         if(this.shadow && this.shadow.isVisible()){
10121             this.shadow.show(this.el);
10122         }
10123         if(this.shim && this.shim.isVisible()){
10124             this.shim.setBounds(x, y, w, h);
10125         }
10126     },
10127
10128     // private
10129     adjustViewport : function(w, h){
10130         if(!w || !h){
10131             w = Roo.lib.Dom.getViewWidth();
10132             h = Roo.lib.Dom.getViewHeight();
10133         }
10134         // cache the size
10135         this.viewSize = [w, h];
10136         if(this.modal && this.mask.isVisible()){
10137             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10138             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10139         }
10140         if(this.isVisible()){
10141             this.constrainXY();
10142         }
10143     },
10144
10145     /**
10146      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10147      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10148      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10149      */
10150     destroy : function(removeEl){
10151         if(this.isVisible()){
10152             this.animateTarget = null;
10153             this.hide();
10154         }
10155         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10156         if(this.tabs){
10157             this.tabs.destroy(removeEl);
10158         }
10159         Roo.destroy(
10160              this.shim,
10161              this.proxy,
10162              this.resizer,
10163              this.close,
10164              this.mask
10165         );
10166         if(this.dd){
10167             this.dd.unreg();
10168         }
10169         if(this.buttons){
10170            for(var i = 0, len = this.buttons.length; i < len; i++){
10171                this.buttons[i].destroy();
10172            }
10173         }
10174         this.el.removeAllListeners();
10175         if(removeEl === true){
10176             this.el.update("");
10177             this.el.remove();
10178         }
10179         Roo.DialogManager.unregister(this);
10180     },
10181
10182     // private
10183     startMove : function(){
10184         if(this.proxyDrag){
10185             this.proxy.show();
10186         }
10187         if(this.constraintoviewport !== false){
10188             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10189         }
10190     },
10191
10192     // private
10193     endMove : function(){
10194         if(!this.proxyDrag){
10195             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10196         }else{
10197             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10198             this.proxy.hide();
10199         }
10200         this.refreshSize();
10201         this.adjustAssets();
10202         this.focus();
10203         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10204     },
10205
10206     /**
10207      * Brings this dialog to the front of any other visible dialogs
10208      * @return {Roo.BasicDialog} this
10209      */
10210     toFront : function(){
10211         Roo.DialogManager.bringToFront(this);
10212         return this;
10213     },
10214
10215     /**
10216      * Sends this dialog to the back (under) of any other visible dialogs
10217      * @return {Roo.BasicDialog} this
10218      */
10219     toBack : function(){
10220         Roo.DialogManager.sendToBack(this);
10221         return this;
10222     },
10223
10224     /**
10225      * Centers this dialog in the viewport
10226      * @return {Roo.BasicDialog} this
10227      */
10228     center : function(){
10229         var xy = this.el.getCenterXY(true);
10230         this.moveTo(xy[0], xy[1]);
10231         return this;
10232     },
10233
10234     /**
10235      * Moves the dialog's top-left corner to the specified point
10236      * @param {Number} x
10237      * @param {Number} y
10238      * @return {Roo.BasicDialog} this
10239      */
10240     moveTo : function(x, y){
10241         this.xy = [x,y];
10242         if(this.isVisible()){
10243             this.el.setXY(this.xy);
10244             this.adjustAssets();
10245         }
10246         return this;
10247     },
10248
10249     /**
10250      * Aligns the dialog to the specified element
10251      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10252      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10253      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10254      * @return {Roo.BasicDialog} this
10255      */
10256     alignTo : function(element, position, offsets){
10257         this.xy = this.el.getAlignToXY(element, position, offsets);
10258         if(this.isVisible()){
10259             this.el.setXY(this.xy);
10260             this.adjustAssets();
10261         }
10262         return this;
10263     },
10264
10265     /**
10266      * Anchors an element to another element and realigns it when the window is resized.
10267      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10268      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10269      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10270      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10271      * is a number, it is used as the buffer delay (defaults to 50ms).
10272      * @return {Roo.BasicDialog} this
10273      */
10274     anchorTo : function(el, alignment, offsets, monitorScroll){
10275         var action = function(){
10276             this.alignTo(el, alignment, offsets);
10277         };
10278         Roo.EventManager.onWindowResize(action, this);
10279         var tm = typeof monitorScroll;
10280         if(tm != 'undefined'){
10281             Roo.EventManager.on(window, 'scroll', action, this,
10282                 {buffer: tm == 'number' ? monitorScroll : 50});
10283         }
10284         action.call(this);
10285         return this;
10286     },
10287
10288     /**
10289      * Returns true if the dialog is visible
10290      * @return {Boolean}
10291      */
10292     isVisible : function(){
10293         return this.el.isVisible();
10294     },
10295
10296     // private
10297     animHide : function(callback){
10298         var b = Roo.get(this.animateTarget).getBox();
10299         this.proxy.show();
10300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10301         this.el.hide();
10302         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10303                     this.hideEl.createDelegate(this, [callback]));
10304     },
10305
10306     /**
10307      * Hides the dialog.
10308      * @param {Function} callback (optional) Function to call when the dialog is hidden
10309      * @return {Roo.BasicDialog} this
10310      */
10311     hide : function(callback){
10312         if (this.fireEvent("beforehide", this) === false){
10313             return;
10314         }
10315         if(this.shadow){
10316             this.shadow.hide();
10317         }
10318         if(this.shim) {
10319           this.shim.hide();
10320         }
10321         // sometimes animateTarget seems to get set.. causing problems...
10322         // this just double checks..
10323         if(this.animateTarget && Roo.get(this.animateTarget)) {
10324            this.animHide(callback);
10325         }else{
10326             this.el.hide();
10327             this.hideEl(callback);
10328         }
10329         return this;
10330     },
10331
10332     // private
10333     hideEl : function(callback){
10334         this.proxy.hide();
10335         if(this.modal){
10336             this.mask.hide();
10337             Roo.get(document.body).removeClass("x-body-masked");
10338         }
10339         this.fireEvent("hide", this);
10340         if(typeof callback == "function"){
10341             callback();
10342         }
10343     },
10344
10345     // private
10346     hideAction : function(){
10347         this.setLeft("-10000px");
10348         this.setTop("-10000px");
10349         this.setStyle("visibility", "hidden");
10350     },
10351
10352     // private
10353     refreshSize : function(){
10354         this.size = this.el.getSize();
10355         this.xy = this.el.getXY();
10356         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10357     },
10358
10359     // private
10360     // z-index is managed by the DialogManager and may be overwritten at any time
10361     setZIndex : function(index){
10362         if(this.modal){
10363             this.mask.setStyle("z-index", index);
10364         }
10365         if(this.shim){
10366             this.shim.setStyle("z-index", ++index);
10367         }
10368         if(this.shadow){
10369             this.shadow.setZIndex(++index);
10370         }
10371         this.el.setStyle("z-index", ++index);
10372         if(this.proxy){
10373             this.proxy.setStyle("z-index", ++index);
10374         }
10375         if(this.resizer){
10376             this.resizer.proxy.setStyle("z-index", ++index);
10377         }
10378
10379         this.lastZIndex = index;
10380     },
10381
10382     /**
10383      * Returns the element for this dialog
10384      * @return {Roo.Element} The underlying dialog Element
10385      */
10386     getEl : function(){
10387         return this.el;
10388     }
10389 });
10390
10391 /**
10392  * @class Roo.DialogManager
10393  * Provides global access to BasicDialogs that have been created and
10394  * support for z-indexing (layering) multiple open dialogs.
10395  */
10396 Roo.DialogManager = function(){
10397     var list = {};
10398     var accessList = [];
10399     var front = null;
10400
10401     // private
10402     var sortDialogs = function(d1, d2){
10403         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10404     };
10405
10406     // private
10407     var orderDialogs = function(){
10408         accessList.sort(sortDialogs);
10409         var seed = Roo.DialogManager.zseed;
10410         for(var i = 0, len = accessList.length; i < len; i++){
10411             var dlg = accessList[i];
10412             if(dlg){
10413                 dlg.setZIndex(seed + (i*10));
10414             }
10415         }
10416     };
10417
10418     return {
10419         /**
10420          * The starting z-index for BasicDialogs (defaults to 9000)
10421          * @type Number The z-index value
10422          */
10423         zseed : 9000,
10424
10425         // private
10426         register : function(dlg){
10427             list[dlg.id] = dlg;
10428             accessList.push(dlg);
10429         },
10430
10431         // private
10432         unregister : function(dlg){
10433             delete list[dlg.id];
10434             var i=0;
10435             var len=0;
10436             if(!accessList.indexOf){
10437                 for(  i = 0, len = accessList.length; i < len; i++){
10438                     if(accessList[i] == dlg){
10439                         accessList.splice(i, 1);
10440                         return;
10441                     }
10442                 }
10443             }else{
10444                  i = accessList.indexOf(dlg);
10445                 if(i != -1){
10446                     accessList.splice(i, 1);
10447                 }
10448             }
10449         },
10450
10451         /**
10452          * Gets a registered dialog by id
10453          * @param {String/Object} id The id of the dialog or a dialog
10454          * @return {Roo.BasicDialog} this
10455          */
10456         get : function(id){
10457             return typeof id == "object" ? id : list[id];
10458         },
10459
10460         /**
10461          * Brings the specified dialog to the front
10462          * @param {String/Object} dlg The id of the dialog or a dialog
10463          * @return {Roo.BasicDialog} this
10464          */
10465         bringToFront : function(dlg){
10466             dlg = this.get(dlg);
10467             if(dlg != front){
10468                 front = dlg;
10469                 dlg._lastAccess = new Date().getTime();
10470                 orderDialogs();
10471             }
10472             return dlg;
10473         },
10474
10475         /**
10476          * Sends the specified dialog to the back
10477          * @param {String/Object} dlg The id of the dialog or a dialog
10478          * @return {Roo.BasicDialog} this
10479          */
10480         sendToBack : function(dlg){
10481             dlg = this.get(dlg);
10482             dlg._lastAccess = -(new Date().getTime());
10483             orderDialogs();
10484             return dlg;
10485         },
10486
10487         /**
10488          * Hides all dialogs
10489          */
10490         hideAll : function(){
10491             for(var id in list){
10492                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10493                     list[id].hide();
10494                 }
10495             }
10496         }
10497     };
10498 }();
10499
10500 /**
10501  * @class Roo.LayoutDialog
10502  * @extends Roo.BasicDialog
10503  * Dialog which provides adjustments for working with a layout in a Dialog.
10504  * Add your necessary layout config options to the dialog's config.<br>
10505  * Example usage (including a nested layout):
10506  * <pre><code>
10507 if(!dialog){
10508     dialog = new Roo.LayoutDialog("download-dlg", {
10509         modal: true,
10510         width:600,
10511         height:450,
10512         shadow:true,
10513         minWidth:500,
10514         minHeight:350,
10515         autoTabs:true,
10516         proxyDrag:true,
10517         // layout config merges with the dialog config
10518         center:{
10519             tabPosition: "top",
10520             alwaysShowTabs: true
10521         }
10522     });
10523     dialog.addKeyListener(27, dialog.hide, dialog);
10524     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10525     dialog.addButton("Build It!", this.getDownload, this);
10526
10527     // we can even add nested layouts
10528     var innerLayout = new Roo.BorderLayout("dl-inner", {
10529         east: {
10530             initialSize: 200,
10531             autoScroll:true,
10532             split:true
10533         },
10534         center: {
10535             autoScroll:true
10536         }
10537     });
10538     innerLayout.beginUpdate();
10539     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10540     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10541     innerLayout.endUpdate(true);
10542
10543     var layout = dialog.getLayout();
10544     layout.beginUpdate();
10545     layout.add("center", new Roo.ContentPanel("standard-panel",
10546                         {title: "Download the Source", fitToFrame:true}));
10547     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10548                {title: "Build your own roo.js"}));
10549     layout.getRegion("center").showPanel(sp);
10550     layout.endUpdate();
10551 }
10552 </code></pre>
10553     * @constructor
10554     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10555     * @param {Object} config configuration options
10556   */
10557 Roo.LayoutDialog = function(el, cfg){
10558     
10559     var config=  cfg;
10560     if (typeof(cfg) == 'undefined') {
10561         config = Roo.apply({}, el);
10562         // not sure why we use documentElement here.. - it should always be body.
10563         // IE7 borks horribly if we use documentElement.
10564         // webkit also does not like documentElement - it creates a body element...
10565         el = Roo.get( document.body || document.documentElement ).createChild();
10566         //config.autoCreate = true;
10567     }
10568     
10569     
10570     config.autoTabs = false;
10571     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10572     this.body.setStyle({overflow:"hidden", position:"relative"});
10573     this.layout = new Roo.BorderLayout(this.body.dom, config);
10574     this.layout.monitorWindowResize = false;
10575     this.el.addClass("x-dlg-auto-layout");
10576     // fix case when center region overwrites center function
10577     this.center = Roo.BasicDialog.prototype.center;
10578     this.on("show", this.layout.layout, this.layout, true);
10579     if (config.items) {
10580         var xitems = config.items;
10581         delete config.items;
10582         Roo.each(xitems, this.addxtype, this);
10583     }
10584     
10585     
10586 };
10587 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10588     /**
10589      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      * @deprecated
10591      */
10592     endUpdate : function(){
10593         this.layout.endUpdate();
10594     },
10595
10596     /**
10597      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10598      *  @deprecated
10599      */
10600     beginUpdate : function(){
10601         this.layout.beginUpdate();
10602     },
10603
10604     /**
10605      * Get the BorderLayout for this dialog
10606      * @return {Roo.BorderLayout}
10607      */
10608     getLayout : function(){
10609         return this.layout;
10610     },
10611
10612     showEl : function(){
10613         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10614         if(Roo.isIE7){
10615             this.layout.layout();
10616         }
10617     },
10618
10619     // private
10620     // Use the syncHeightBeforeShow config option to control this automatically
10621     syncBodyHeight : function(){
10622         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10623         if(this.layout){this.layout.layout();}
10624     },
10625     
10626       /**
10627      * Add an xtype element (actually adds to the layout.)
10628      * @return {Object} xdata xtype object data.
10629      */
10630     
10631     addxtype : function(c) {
10632         return this.layout.addxtype(c);
10633     }
10634 });/*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644  
10645 /**
10646  * @class Roo.MessageBox
10647  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10648  * Example usage:
10649  *<pre><code>
10650 // Basic alert:
10651 Roo.Msg.alert('Status', 'Changes saved successfully.');
10652
10653 // Prompt for user data:
10654 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10655     if (btn == 'ok'){
10656         // process text value...
10657     }
10658 });
10659
10660 // Show a dialog using config options:
10661 Roo.Msg.show({
10662    title:'Save Changes?',
10663    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10664    buttons: Roo.Msg.YESNOCANCEL,
10665    fn: processResult,
10666    animEl: 'elId'
10667 });
10668 </code></pre>
10669  * @singleton
10670  */
10671 Roo.MessageBox = function(){
10672     var dlg, opt, mask, waitTimer;
10673     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10674     var buttons, activeTextEl, bwidth;
10675
10676     // private
10677     var handleButton = function(button){
10678         dlg.hide();
10679         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10680     };
10681
10682     // private
10683     var handleHide = function(){
10684         if(opt && opt.cls){
10685             dlg.el.removeClass(opt.cls);
10686         }
10687         if(waitTimer){
10688             Roo.TaskMgr.stop(waitTimer);
10689             waitTimer = null;
10690         }
10691     };
10692
10693     // private
10694     var updateButtons = function(b){
10695         var width = 0;
10696         if(!b){
10697             buttons["ok"].hide();
10698             buttons["cancel"].hide();
10699             buttons["yes"].hide();
10700             buttons["no"].hide();
10701             dlg.footer.dom.style.display = 'none';
10702             return width;
10703         }
10704         dlg.footer.dom.style.display = '';
10705         for(var k in buttons){
10706             if(typeof buttons[k] != "function"){
10707                 if(b[k]){
10708                     buttons[k].show();
10709                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10710                     width += buttons[k].el.getWidth()+15;
10711                 }else{
10712                     buttons[k].hide();
10713                 }
10714             }
10715         }
10716         return width;
10717     };
10718
10719     // private
10720     var handleEsc = function(d, k, e){
10721         if(opt && opt.closable !== false){
10722             dlg.hide();
10723         }
10724         if(e){
10725             e.stopEvent();
10726         }
10727     };
10728
10729     return {
10730         /**
10731          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10732          * @return {Roo.BasicDialog} The BasicDialog element
10733          */
10734         getDialog : function(){
10735            if(!dlg){
10736                 dlg = new Roo.BasicDialog("x-msg-box", {
10737                     autoCreate : true,
10738                     shadow: true,
10739                     draggable: true,
10740                     resizable:false,
10741                     constraintoviewport:false,
10742                     fixedcenter:true,
10743                     collapsible : false,
10744                     shim:true,
10745                     modal: true,
10746                     width:400, height:100,
10747                     buttonAlign:"center",
10748                     closeClick : function(){
10749                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10750                             handleButton("no");
10751                         }else{
10752                             handleButton("cancel");
10753                         }
10754                     }
10755                 });
10756                 dlg.on("hide", handleHide);
10757                 mask = dlg.mask;
10758                 dlg.addKeyListener(27, handleEsc);
10759                 buttons = {};
10760                 var bt = this.buttonText;
10761                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10762                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10763                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10764                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10765                 bodyEl = dlg.body.createChild({
10766
10767                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10768                 });
10769                 msgEl = bodyEl.dom.firstChild;
10770                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10771                 textboxEl.enableDisplayMode();
10772                 textboxEl.addKeyListener([10,13], function(){
10773                     if(dlg.isVisible() && opt && opt.buttons){
10774                         if(opt.buttons.ok){
10775                             handleButton("ok");
10776                         }else if(opt.buttons.yes){
10777                             handleButton("yes");
10778                         }
10779                     }
10780                 });
10781                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10782                 textareaEl.enableDisplayMode();
10783                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10784                 progressEl.enableDisplayMode();
10785                 var pf = progressEl.dom.firstChild;
10786                 if (pf) {
10787                     pp = Roo.get(pf.firstChild);
10788                     pp.setHeight(pf.offsetHeight);
10789                 }
10790                 
10791             }
10792             return dlg;
10793         },
10794
10795         /**
10796          * Updates the message box body text
10797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10798          * the XHTML-compliant non-breaking space character '&amp;#160;')
10799          * @return {Roo.MessageBox} This message box
10800          */
10801         updateText : function(text){
10802             if(!dlg.isVisible() && !opt.width){
10803                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10804             }
10805             msgEl.innerHTML = text || '&#160;';
10806       
10807             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10808             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10809             var w = Math.max(
10810                     Math.min(opt.width || cw , this.maxWidth), 
10811                     Math.max(opt.minWidth || this.minWidth, bwidth)
10812             );
10813             if(opt.prompt){
10814                 activeTextEl.setWidth(w);
10815             }
10816             if(dlg.isVisible()){
10817                 dlg.fixedcenter = false;
10818             }
10819             // to big, make it scroll. = But as usual stupid IE does not support
10820             // !important..
10821             
10822             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10823                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10824                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10825             } else {
10826                 bodyEl.dom.style.height = '';
10827                 bodyEl.dom.style.overflowY = '';
10828             }
10829             if (cw > w) {
10830                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10831             } else {
10832                 bodyEl.dom.style.overflowX = '';
10833             }
10834             
10835             dlg.setContentSize(w, bodyEl.getHeight());
10836             if(dlg.isVisible()){
10837                 dlg.fixedcenter = true;
10838             }
10839             return this;
10840         },
10841
10842         /**
10843          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10844          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10845          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10846          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10847          * @return {Roo.MessageBox} This message box
10848          */
10849         updateProgress : function(value, text){
10850             if(text){
10851                 this.updateText(text);
10852             }
10853             if (pp) { // weird bug on my firefox - for some reason this is not defined
10854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10855             }
10856             return this;
10857         },        
10858
10859         /**
10860          * Returns true if the message box is currently displayed
10861          * @return {Boolean} True if the message box is visible, else false
10862          */
10863         isVisible : function(){
10864             return dlg && dlg.isVisible();  
10865         },
10866
10867         /**
10868          * Hides the message box if it is displayed
10869          */
10870         hide : function(){
10871             if(this.isVisible()){
10872                 dlg.hide();
10873             }  
10874         },
10875
10876         /**
10877          * Displays a new message box, or reinitializes an existing message box, based on the config options
10878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10879          * The following config object properties are supported:
10880          * <pre>
10881 Property    Type             Description
10882 ----------  ---------------  ------------------------------------------------------------------------------------
10883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10884                                    closes (defaults to undefined)
10885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10888                                    progress and wait dialogs will ignore this property and always hide the
10889                                    close button as they can only be closed programmatically.
10890 cls               String           A custom CSS class to apply to the message box element
10891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10892                                    displayed (defaults to 75)
10893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10894                                    function will be btn (the name of the button that was clicked, if applicable,
10895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10896                                    Progress and wait dialogs will ignore this option since they do not respond to
10897                                    user actions and can only be closed programmatically, so any required function
10898                                    should be called by the same code after it closes the dialog.
10899 icon              String           A CSS class that provides a background image to be used as an icon for
10900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10903 modal             Boolean          False to allow user interaction with the page while the message box is
10904                                    displayed (defaults to true)
10905 msg               String           A string that will replace the existing message box body text (defaults
10906                                    to the XHTML-compliant non-breaking space character '&#160;')
10907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10908 progress          Boolean          True to display a progress bar (defaults to false)
10909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10912 title             String           The title text
10913 value             String           The string value to set into the active textbox element if displayed
10914 wait              Boolean          True to display a progress bar (defaults to false)
10915 width             Number           The width of the dialog in pixels
10916 </pre>
10917          *
10918          * Example usage:
10919          * <pre><code>
10920 Roo.Msg.show({
10921    title: 'Address',
10922    msg: 'Please enter your address:',
10923    width: 300,
10924    buttons: Roo.MessageBox.OKCANCEL,
10925    multiline: true,
10926    fn: saveAddress,
10927    animEl: 'addAddressBtn'
10928 });
10929 </code></pre>
10930          * @param {Object} config Configuration options
10931          * @return {Roo.MessageBox} This message box
10932          */
10933         show : function(options)
10934         {
10935             
10936             // this causes nightmares if you show one dialog after another
10937             // especially on callbacks..
10938              
10939             if(this.isVisible()){
10940                 
10941                 this.hide();
10942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10944                 Roo.log("New Dialog Message:" +  options.msg )
10945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10947                 
10948             }
10949             var d = this.getDialog();
10950             opt = options;
10951             d.setTitle(opt.title || "&#160;");
10952             d.close.setDisplayed(opt.closable !== false);
10953             activeTextEl = textboxEl;
10954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10955             if(opt.prompt){
10956                 if(opt.multiline){
10957                     textboxEl.hide();
10958                     textareaEl.show();
10959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10960                         opt.multiline : this.defaultTextHeight);
10961                     activeTextEl = textareaEl;
10962                 }else{
10963                     textboxEl.show();
10964                     textareaEl.hide();
10965                 }
10966             }else{
10967                 textboxEl.hide();
10968                 textareaEl.hide();
10969             }
10970             progressEl.setDisplayed(opt.progress === true);
10971             this.updateProgress(0);
10972             activeTextEl.dom.value = opt.value || "";
10973             if(opt.prompt){
10974                 dlg.setDefaultButton(activeTextEl);
10975             }else{
10976                 var bs = opt.buttons;
10977                 var db = null;
10978                 if(bs && bs.ok){
10979                     db = buttons["ok"];
10980                 }else if(bs && bs.yes){
10981                     db = buttons["yes"];
10982                 }
10983                 dlg.setDefaultButton(db);
10984             }
10985             bwidth = updateButtons(opt.buttons);
10986             this.updateText(opt.msg);
10987             if(opt.cls){
10988                 d.el.addClass(opt.cls);
10989             }
10990             d.proxyDrag = opt.proxyDrag === true;
10991             d.modal = opt.modal !== false;
10992             d.mask = opt.modal !== false ? mask : false;
10993             if(!d.isVisible()){
10994                 // force it to the end of the z-index stack so it gets a cursor in FF
10995                 document.body.appendChild(dlg.el.dom);
10996                 d.animateTarget = null;
10997                 d.show(options.animEl);
10998             }
10999             return this;
11000         },
11001
11002         /**
11003          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11004          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11005          * and closing the message box when the process is complete.
11006          * @param {String} title The title bar text
11007          * @param {String} msg The message box body text
11008          * @return {Roo.MessageBox} This message box
11009          */
11010         progress : function(title, msg){
11011             this.show({
11012                 title : title,
11013                 msg : msg,
11014                 buttons: false,
11015                 progress:true,
11016                 closable:false,
11017                 minWidth: this.minProgressWidth,
11018                 modal : true
11019             });
11020             return this;
11021         },
11022
11023         /**
11024          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11025          * If a callback function is passed it will be called after the user clicks the button, and the
11026          * id of the button that was clicked will be passed as the only parameter to the callback
11027          * (could also be the top-right close button).
11028          * @param {String} title The title bar text
11029          * @param {String} msg The message box body text
11030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11031          * @param {Object} scope (optional) The scope of the callback function
11032          * @return {Roo.MessageBox} This message box
11033          */
11034         alert : function(title, msg, fn, scope){
11035             this.show({
11036                 title : title,
11037                 msg : msg,
11038                 buttons: this.OK,
11039                 fn: fn,
11040                 scope : scope,
11041                 modal : true
11042             });
11043             return this;
11044         },
11045
11046         /**
11047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11049          * You are responsible for closing the message box when the process is complete.
11050          * @param {String} msg The message box body text
11051          * @param {String} title (optional) The title bar text
11052          * @return {Roo.MessageBox} This message box
11053          */
11054         wait : function(msg, title){
11055             this.show({
11056                 title : title,
11057                 msg : msg,
11058                 buttons: false,
11059                 closable:false,
11060                 progress:true,
11061                 modal:true,
11062                 width:300,
11063                 wait:true
11064             });
11065             waitTimer = Roo.TaskMgr.start({
11066                 run: function(i){
11067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11068                 },
11069                 interval: 1000
11070             });
11071             return this;
11072         },
11073
11074         /**
11075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11078          * @param {String} title The title bar text
11079          * @param {String} msg The message box body text
11080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081          * @param {Object} scope (optional) The scope of the callback function
11082          * @return {Roo.MessageBox} This message box
11083          */
11084         confirm : function(title, msg, fn, scope){
11085             this.show({
11086                 title : title,
11087                 msg : msg,
11088                 buttons: this.YESNO,
11089                 fn: fn,
11090                 scope : scope,
11091                 modal : true
11092             });
11093             return this;
11094         },
11095
11096         /**
11097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11100          * (could also be the top-right close button) and the text that was entered will be passed as the two
11101          * parameters to the callback.
11102          * @param {String} title The title bar text
11103          * @param {String} msg The message box body text
11104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11105          * @param {Object} scope (optional) The scope of the callback function
11106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11108          * @return {Roo.MessageBox} This message box
11109          */
11110         prompt : function(title, msg, fn, scope, multiline){
11111             this.show({
11112                 title : title,
11113                 msg : msg,
11114                 buttons: this.OKCANCEL,
11115                 fn: fn,
11116                 minWidth:250,
11117                 scope : scope,
11118                 prompt:true,
11119                 multiline: multiline,
11120                 modal : true
11121             });
11122             return this;
11123         },
11124
11125         /**
11126          * Button config that displays a single OK button
11127          * @type Object
11128          */
11129         OK : {ok:true},
11130         /**
11131          * Button config that displays Yes and No buttons
11132          * @type Object
11133          */
11134         YESNO : {yes:true, no:true},
11135         /**
11136          * Button config that displays OK and Cancel buttons
11137          * @type Object
11138          */
11139         OKCANCEL : {ok:true, cancel:true},
11140         /**
11141          * Button config that displays Yes, No and Cancel buttons
11142          * @type Object
11143          */
11144         YESNOCANCEL : {yes:true, no:true, cancel:true},
11145
11146         /**
11147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11148          * @type Number
11149          */
11150         defaultTextHeight : 75,
11151         /**
11152          * The maximum width in pixels of the message box (defaults to 600)
11153          * @type Number
11154          */
11155         maxWidth : 600,
11156         /**
11157          * The minimum width in pixels of the message box (defaults to 100)
11158          * @type Number
11159          */
11160         minWidth : 100,
11161         /**
11162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11164          * @type Number
11165          */
11166         minProgressWidth : 250,
11167         /**
11168          * An object containing the default button text strings that can be overriden for localized language support.
11169          * Supported properties are: ok, cancel, yes and no.
11170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11171          * @type Object
11172          */
11173         buttonText : {
11174             ok : "OK",
11175             cancel : "Cancel",
11176             yes : "Yes",
11177             no : "No"
11178         }
11179     };
11180 }();
11181
11182 /**
11183  * Shorthand for {@link Roo.MessageBox}
11184  */
11185 Roo.Msg = Roo.MessageBox;/*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195 /**
11196  * @class Roo.QuickTips
11197  * Provides attractive and customizable tooltips for any element.
11198  * @singleton
11199  */
11200 Roo.QuickTips = function(){
11201     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11202     var ce, bd, xy, dd;
11203     var visible = false, disabled = true, inited = false;
11204     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11205     
11206     var onOver = function(e){
11207         if(disabled){
11208             return;
11209         }
11210         var t = e.getTarget();
11211         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11212             return;
11213         }
11214         if(ce && t == ce.el){
11215             clearTimeout(hideProc);
11216             return;
11217         }
11218         if(t && tagEls[t.id]){
11219             tagEls[t.id].el = t;
11220             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11221             return;
11222         }
11223         var ttp, et = Roo.fly(t);
11224         var ns = cfg.namespace;
11225         if(tm.interceptTitles && t.title){
11226             ttp = t.title;
11227             t.qtip = ttp;
11228             t.removeAttribute("title");
11229             e.preventDefault();
11230         }else{
11231             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11232         }
11233         if(ttp){
11234             showProc = show.defer(tm.showDelay, tm, [{
11235                 el: t, 
11236                 text: ttp.replace(/\\n/g,'<br/>'),
11237                 width: et.getAttributeNS(ns, cfg.width),
11238                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11239                 title: et.getAttributeNS(ns, cfg.title),
11240                     cls: et.getAttributeNS(ns, cfg.cls)
11241             }]);
11242         }
11243     };
11244     
11245     var onOut = function(e){
11246         clearTimeout(showProc);
11247         var t = e.getTarget();
11248         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11249             hideProc = setTimeout(hide, tm.hideDelay);
11250         }
11251     };
11252     
11253     var onMove = function(e){
11254         if(disabled){
11255             return;
11256         }
11257         xy = e.getXY();
11258         xy[1] += 18;
11259         if(tm.trackMouse && ce){
11260             el.setXY(xy);
11261         }
11262     };
11263     
11264     var onDown = function(e){
11265         clearTimeout(showProc);
11266         clearTimeout(hideProc);
11267         if(!e.within(el)){
11268             if(tm.hideOnClick){
11269                 hide();
11270                 tm.disable();
11271                 tm.enable.defer(100, tm);
11272             }
11273         }
11274     };
11275     
11276     var getPad = function(){
11277         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11278     };
11279
11280     var show = function(o){
11281         if(disabled){
11282             return;
11283         }
11284         clearTimeout(dismissProc);
11285         ce = o;
11286         if(removeCls){ // in case manually hidden
11287             el.removeClass(removeCls);
11288             removeCls = null;
11289         }
11290         if(ce.cls){
11291             el.addClass(ce.cls);
11292             removeCls = ce.cls;
11293         }
11294         if(ce.title){
11295             tipTitle.update(ce.title);
11296             tipTitle.show();
11297         }else{
11298             tipTitle.update('');
11299             tipTitle.hide();
11300         }
11301         el.dom.style.width  = tm.maxWidth+'px';
11302         //tipBody.dom.style.width = '';
11303         tipBodyText.update(o.text);
11304         var p = getPad(), w = ce.width;
11305         if(!w){
11306             var td = tipBodyText.dom;
11307             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11308             if(aw > tm.maxWidth){
11309                 w = tm.maxWidth;
11310             }else if(aw < tm.minWidth){
11311                 w = tm.minWidth;
11312             }else{
11313                 w = aw;
11314             }
11315         }
11316         //tipBody.setWidth(w);
11317         el.setWidth(parseInt(w, 10) + p);
11318         if(ce.autoHide === false){
11319             close.setDisplayed(true);
11320             if(dd){
11321                 dd.unlock();
11322             }
11323         }else{
11324             close.setDisplayed(false);
11325             if(dd){
11326                 dd.lock();
11327             }
11328         }
11329         if(xy){
11330             el.avoidY = xy[1]-18;
11331             el.setXY(xy);
11332         }
11333         if(tm.animate){
11334             el.setOpacity(.1);
11335             el.setStyle("visibility", "visible");
11336             el.fadeIn({callback: afterShow});
11337         }else{
11338             afterShow();
11339         }
11340     };
11341     
11342     var afterShow = function(){
11343         if(ce){
11344             el.show();
11345             esc.enable();
11346             if(tm.autoDismiss && ce.autoHide !== false){
11347                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11348             }
11349         }
11350     };
11351     
11352     var hide = function(noanim){
11353         clearTimeout(dismissProc);
11354         clearTimeout(hideProc);
11355         ce = null;
11356         if(el.isVisible()){
11357             esc.disable();
11358             if(noanim !== true && tm.animate){
11359                 el.fadeOut({callback: afterHide});
11360             }else{
11361                 afterHide();
11362             } 
11363         }
11364     };
11365     
11366     var afterHide = function(){
11367         el.hide();
11368         if(removeCls){
11369             el.removeClass(removeCls);
11370             removeCls = null;
11371         }
11372     };
11373     
11374     return {
11375         /**
11376         * @cfg {Number} minWidth
11377         * The minimum width of the quick tip (defaults to 40)
11378         */
11379        minWidth : 40,
11380         /**
11381         * @cfg {Number} maxWidth
11382         * The maximum width of the quick tip (defaults to 300)
11383         */
11384        maxWidth : 300,
11385         /**
11386         * @cfg {Boolean} interceptTitles
11387         * True to automatically use the element's DOM title value if available (defaults to false)
11388         */
11389        interceptTitles : false,
11390         /**
11391         * @cfg {Boolean} trackMouse
11392         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11393         */
11394        trackMouse : false,
11395         /**
11396         * @cfg {Boolean} hideOnClick
11397         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11398         */
11399        hideOnClick : true,
11400         /**
11401         * @cfg {Number} showDelay
11402         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11403         */
11404        showDelay : 500,
11405         /**
11406         * @cfg {Number} hideDelay
11407         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11408         */
11409        hideDelay : 200,
11410         /**
11411         * @cfg {Boolean} autoHide
11412         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11413         * Used in conjunction with hideDelay.
11414         */
11415        autoHide : true,
11416         /**
11417         * @cfg {Boolean}
11418         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11419         * (defaults to true).  Used in conjunction with autoDismissDelay.
11420         */
11421        autoDismiss : true,
11422         /**
11423         * @cfg {Number}
11424         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11425         */
11426        autoDismissDelay : 5000,
11427        /**
11428         * @cfg {Boolean} animate
11429         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11430         */
11431        animate : false,
11432
11433        /**
11434         * @cfg {String} title
11435         * Title text to display (defaults to '').  This can be any valid HTML markup.
11436         */
11437         title: '',
11438        /**
11439         * @cfg {String} text
11440         * Body text to display (defaults to '').  This can be any valid HTML markup.
11441         */
11442         text : '',
11443        /**
11444         * @cfg {String} cls
11445         * A CSS class to apply to the base quick tip element (defaults to '').
11446         */
11447         cls : '',
11448        /**
11449         * @cfg {Number} width
11450         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11451         * minWidth or maxWidth.
11452         */
11453         width : null,
11454
11455     /**
11456      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11457      * or display QuickTips in a page.
11458      */
11459        init : function(){
11460           tm = Roo.QuickTips;
11461           cfg = tm.tagConfig;
11462           if(!inited){
11463               if(!Roo.isReady){ // allow calling of init() before onReady
11464                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11465                   return;
11466               }
11467               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11468               el.fxDefaults = {stopFx: true};
11469               // maximum custom styling
11470               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11471               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11472               tipTitle = el.child('h3');
11473               tipTitle.enableDisplayMode("block");
11474               tipBody = el.child('div.x-tip-bd');
11475               tipBodyText = el.child('div.x-tip-bd-inner');
11476               //bdLeft = el.child('div.x-tip-bd-left');
11477               //bdRight = el.child('div.x-tip-bd-right');
11478               close = el.child('div.x-tip-close');
11479               close.enableDisplayMode("block");
11480               close.on("click", hide);
11481               var d = Roo.get(document);
11482               d.on("mousedown", onDown);
11483               d.on("mouseover", onOver);
11484               d.on("mouseout", onOut);
11485               d.on("mousemove", onMove);
11486               esc = d.addKeyListener(27, hide);
11487               esc.disable();
11488               if(Roo.dd.DD){
11489                   dd = el.initDD("default", null, {
11490                       onDrag : function(){
11491                           el.sync();  
11492                       }
11493                   });
11494                   dd.setHandleElId(tipTitle.id);
11495                   dd.lock();
11496               }
11497               inited = true;
11498           }
11499           this.enable(); 
11500        },
11501
11502     /**
11503      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11504      * are supported:
11505      * <pre>
11506 Property    Type                   Description
11507 ----------  ---------------------  ------------------------------------------------------------------------
11508 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11509      * </ul>
11510      * @param {Object} config The config object
11511      */
11512        register : function(config){
11513            var cs = config instanceof Array ? config : arguments;
11514            for(var i = 0, len = cs.length; i < len; i++) {
11515                var c = cs[i];
11516                var target = c.target;
11517                if(target){
11518                    if(target instanceof Array){
11519                        for(var j = 0, jlen = target.length; j < jlen; j++){
11520                            tagEls[target[j]] = c;
11521                        }
11522                    }else{
11523                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11524                    }
11525                }
11526            }
11527        },
11528
11529     /**
11530      * Removes this quick tip from its element and destroys it.
11531      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11532      */
11533        unregister : function(el){
11534            delete tagEls[Roo.id(el)];
11535        },
11536
11537     /**
11538      * Enable this quick tip.
11539      */
11540        enable : function(){
11541            if(inited && disabled){
11542                locks.pop();
11543                if(locks.length < 1){
11544                    disabled = false;
11545                }
11546            }
11547        },
11548
11549     /**
11550      * Disable this quick tip.
11551      */
11552        disable : function(){
11553           disabled = true;
11554           clearTimeout(showProc);
11555           clearTimeout(hideProc);
11556           clearTimeout(dismissProc);
11557           if(ce){
11558               hide(true);
11559           }
11560           locks.push(1);
11561        },
11562
11563     /**
11564      * Returns true if the quick tip is enabled, else false.
11565      */
11566        isEnabled : function(){
11567             return !disabled;
11568        },
11569
11570         // private
11571        tagConfig : {
11572            namespace : "roo", // was ext?? this may break..
11573            alt_namespace : "ext",
11574            attribute : "qtip",
11575            width : "width",
11576            target : "target",
11577            title : "qtitle",
11578            hide : "hide",
11579            cls : "qclass"
11580        }
11581    };
11582 }();
11583
11584 // backwards compat
11585 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11586  * Based on:
11587  * Ext JS Library 1.1.1
11588  * Copyright(c) 2006-2007, Ext JS, LLC.
11589  *
11590  * Originally Released Under LGPL - original licence link has changed is not relivant.
11591  *
11592  * Fork - LGPL
11593  * <script type="text/javascript">
11594  */
11595  
11596
11597 /**
11598  * @class Roo.tree.TreePanel
11599  * @extends Roo.data.Tree
11600
11601  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11602  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11603  * @cfg {Boolean} enableDD true to enable drag and drop
11604  * @cfg {Boolean} enableDrag true to enable just drag
11605  * @cfg {Boolean} enableDrop true to enable just drop
11606  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11607  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11608  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11609  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11610  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11611  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11612  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11613  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11614  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11615  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11616  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11617  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11618  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11619  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11620  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11621  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11622  * 
11623  * @constructor
11624  * @param {String/HTMLElement/Element} el The container element
11625  * @param {Object} config
11626  */
11627 Roo.tree.TreePanel = function(el, config){
11628     var root = false;
11629     var loader = false;
11630     if (config.root) {
11631         root = config.root;
11632         delete config.root;
11633     }
11634     if (config.loader) {
11635         loader = config.loader;
11636         delete config.loader;
11637     }
11638     
11639     Roo.apply(this, config);
11640     Roo.tree.TreePanel.superclass.constructor.call(this);
11641     this.el = Roo.get(el);
11642     this.el.addClass('x-tree');
11643     //console.log(root);
11644     if (root) {
11645         this.setRootNode( Roo.factory(root, Roo.tree));
11646     }
11647     if (loader) {
11648         this.loader = Roo.factory(loader, Roo.tree);
11649     }
11650    /**
11651     * Read-only. The id of the container element becomes this TreePanel's id.
11652     */
11653     this.id = this.el.id;
11654     this.addEvents({
11655         /**
11656         * @event beforeload
11657         * Fires before a node is loaded, return false to cancel
11658         * @param {Node} node The node being loaded
11659         */
11660         "beforeload" : true,
11661         /**
11662         * @event load
11663         * Fires when a node is loaded
11664         * @param {Node} node The node that was loaded
11665         */
11666         "load" : true,
11667         /**
11668         * @event textchange
11669         * Fires when the text for a node is changed
11670         * @param {Node} node The node
11671         * @param {String} text The new text
11672         * @param {String} oldText The old text
11673         */
11674         "textchange" : true,
11675         /**
11676         * @event beforeexpand
11677         * Fires before a node is expanded, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforeexpand" : true,
11683         /**
11684         * @event beforecollapse
11685         * Fires before a node is collapsed, return false to cancel.
11686         * @param {Node} node The node
11687         * @param {Boolean} deep
11688         * @param {Boolean} anim
11689         */
11690         "beforecollapse" : true,
11691         /**
11692         * @event expand
11693         * Fires when a node is expanded
11694         * @param {Node} node The node
11695         */
11696         "expand" : true,
11697         /**
11698         * @event disabledchange
11699         * Fires when the disabled status of a node changes
11700         * @param {Node} node The node
11701         * @param {Boolean} disabled
11702         */
11703         "disabledchange" : true,
11704         /**
11705         * @event collapse
11706         * Fires when a node is collapsed
11707         * @param {Node} node The node
11708         */
11709         "collapse" : true,
11710         /**
11711         * @event beforeclick
11712         * Fires before click processing on a node. Return false to cancel the default action.
11713         * @param {Node} node The node
11714         * @param {Roo.EventObject} e The event object
11715         */
11716         "beforeclick":true,
11717         /**
11718         * @event checkchange
11719         * Fires when a node with a checkbox's checked property changes
11720         * @param {Node} this This node
11721         * @param {Boolean} checked
11722         */
11723         "checkchange":true,
11724         /**
11725         * @event click
11726         * Fires when a node is clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "click":true,
11731         /**
11732         * @event dblclick
11733         * Fires when a node is double clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "dblclick":true,
11738         /**
11739         * @event contextmenu
11740         * Fires when a node is right clicked
11741         * @param {Node} node The node
11742         * @param {Roo.EventObject} e The event object
11743         */
11744         "contextmenu":true,
11745         /**
11746         * @event beforechildrenrendered
11747         * Fires right before the child nodes for a node are rendered
11748         * @param {Node} node The node
11749         */
11750         "beforechildrenrendered":true,
11751         /**
11752         * @event startdrag
11753         * Fires when a node starts being dragged
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */ 
11758        "startdrag" : true,
11759        /**
11760         * @event enddrag
11761         * Fires when a drag operation is complete
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {event} e The raw browser event
11765         */
11766        "enddrag" : true,
11767        /**
11768         * @event dragdrop
11769         * Fires when a dragged node is dropped on a valid DD target
11770         * @param {Roo.tree.TreePanel} this
11771         * @param {Roo.tree.TreeNode} node
11772         * @param {DD} dd The dd it was dropped on
11773         * @param {event} e The raw browser event
11774         */
11775        "dragdrop" : true,
11776        /**
11777         * @event beforenodedrop
11778         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11779         * passed to handlers has the following properties:<br />
11780         * <ul style="padding:5px;padding-left:16px;">
11781         * <li>tree - The TreePanel</li>
11782         * <li>target - The node being targeted for the drop</li>
11783         * <li>data - The drag data from the drag source</li>
11784         * <li>point - The point of the drop - append, above or below</li>
11785         * <li>source - The drag source</li>
11786         * <li>rawEvent - Raw mouse event</li>
11787         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11788         * to be inserted by setting them on this object.</li>
11789         * <li>cancel - Set this to true to cancel the drop.</li>
11790         * </ul>
11791         * @param {Object} dropEvent
11792         */
11793        "beforenodedrop" : true,
11794        /**
11795         * @event nodedrop
11796         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Dropped node(s).</li>
11806         * </ul>
11807         * @param {Object} dropEvent
11808         */
11809        "nodedrop" : true,
11810         /**
11811         * @event nodedragover
11812         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11813         * passed to handlers has the following properties:<br />
11814         * <ul style="padding:5px;padding-left:16px;">
11815         * <li>tree - The TreePanel</li>
11816         * <li>target - The node being targeted for the drop</li>
11817         * <li>data - The drag data from the drag source</li>
11818         * <li>point - The point of the drop - append, above or below</li>
11819         * <li>source - The drag source</li>
11820         * <li>rawEvent - Raw mouse event</li>
11821         * <li>dropNode - Drop node(s) provided by the source.</li>
11822         * <li>cancel - Set this to true to signal drop not allowed.</li>
11823         * </ul>
11824         * @param {Object} dragOverEvent
11825         */
11826        "nodedragover" : true
11827         
11828     });
11829     if(this.singleExpand){
11830        this.on("beforeexpand", this.restrictExpand, this);
11831     }
11832     if (this.editor) {
11833         this.editor.tree = this;
11834         this.editor = Roo.factory(this.editor, Roo.tree);
11835     }
11836     
11837     if (this.selModel) {
11838         this.selModel = Roo.factory(this.selModel, Roo.tree);
11839     }
11840    
11841 };
11842 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11843     rootVisible : true,
11844     animate: Roo.enableFx,
11845     lines : true,
11846     enableDD : false,
11847     hlDrop : Roo.enableFx,
11848   
11849     renderer: false,
11850     
11851     rendererTip: false,
11852     // private
11853     restrictExpand : function(node){
11854         var p = node.parentNode;
11855         if(p){
11856             if(p.expandedChild && p.expandedChild.parentNode == p){
11857                 p.expandedChild.collapse();
11858             }
11859             p.expandedChild = node;
11860         }
11861     },
11862
11863     // private override
11864     setRootNode : function(node){
11865         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11866         if(!this.rootVisible){
11867             node.ui = new Roo.tree.RootTreeNodeUI(node);
11868         }
11869         return node;
11870     },
11871
11872     /**
11873      * Returns the container element for this TreePanel
11874      */
11875     getEl : function(){
11876         return this.el;
11877     },
11878
11879     /**
11880      * Returns the default TreeLoader for this TreePanel
11881      */
11882     getLoader : function(){
11883         return this.loader;
11884     },
11885
11886     /**
11887      * Expand all nodes
11888      */
11889     expandAll : function(){
11890         this.root.expand(true);
11891     },
11892
11893     /**
11894      * Collapse all nodes
11895      */
11896     collapseAll : function(){
11897         this.root.collapse(true);
11898     },
11899
11900     /**
11901      * Returns the selection model used by this TreePanel
11902      */
11903     getSelectionModel : function(){
11904         if(!this.selModel){
11905             this.selModel = new Roo.tree.DefaultSelectionModel();
11906         }
11907         return this.selModel;
11908     },
11909
11910     /**
11911      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11912      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11913      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11914      * @return {Array}
11915      */
11916     getChecked : function(a, startNode){
11917         startNode = startNode || this.root;
11918         var r = [];
11919         var f = function(){
11920             if(this.attributes.checked){
11921                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11922             }
11923         }
11924         startNode.cascade(f);
11925         return r;
11926     },
11927
11928     /**
11929      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11930      * @param {String} path
11931      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11932      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11933      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11934      */
11935     expandPath : function(path, attr, callback){
11936         attr = attr || "id";
11937         var keys = path.split(this.pathSeparator);
11938         var curNode = this.root;
11939         if(curNode.attributes[attr] != keys[1]){ // invalid root
11940             if(callback){
11941                 callback(false, null);
11942             }
11943             return;
11944         }
11945         var index = 1;
11946         var f = function(){
11947             if(++index == keys.length){
11948                 if(callback){
11949                     callback(true, curNode);
11950                 }
11951                 return;
11952             }
11953             var c = curNode.findChild(attr, keys[index]);
11954             if(!c){
11955                 if(callback){
11956                     callback(false, curNode);
11957                 }
11958                 return;
11959             }
11960             curNode = c;
11961             c.expand(false, false, f);
11962         };
11963         curNode.expand(false, false, f);
11964     },
11965
11966     /**
11967      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11968      * @param {String} path
11969      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11970      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11971      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11972      */
11973     selectPath : function(path, attr, callback){
11974         attr = attr || "id";
11975         var keys = path.split(this.pathSeparator);
11976         var v = keys.pop();
11977         if(keys.length > 0){
11978             var f = function(success, node){
11979                 if(success && node){
11980                     var n = node.findChild(attr, v);
11981                     if(n){
11982                         n.select();
11983                         if(callback){
11984                             callback(true, n);
11985                         }
11986                     }else if(callback){
11987                         callback(false, n);
11988                     }
11989                 }else{
11990                     if(callback){
11991                         callback(false, n);
11992                     }
11993                 }
11994             };
11995             this.expandPath(keys.join(this.pathSeparator), attr, f);
11996         }else{
11997             this.root.select();
11998             if(callback){
11999                 callback(true, this.root);
12000             }
12001         }
12002     },
12003
12004     getTreeEl : function(){
12005         return this.el;
12006     },
12007
12008     /**
12009      * Trigger rendering of this TreePanel
12010      */
12011     render : function(){
12012         if (this.innerCt) {
12013             return this; // stop it rendering more than once!!
12014         }
12015         
12016         this.innerCt = this.el.createChild({tag:"ul",
12017                cls:"x-tree-root-ct " +
12018                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12019
12020         if(this.containerScroll){
12021             Roo.dd.ScrollManager.register(this.el);
12022         }
12023         if((this.enableDD || this.enableDrop) && !this.dropZone){
12024            /**
12025             * The dropZone used by this tree if drop is enabled
12026             * @type Roo.tree.TreeDropZone
12027             */
12028              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12029                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12030            });
12031         }
12032         if((this.enableDD || this.enableDrag) && !this.dragZone){
12033            /**
12034             * The dragZone used by this tree if drag is enabled
12035             * @type Roo.tree.TreeDragZone
12036             */
12037             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12038                ddGroup: this.ddGroup || "TreeDD",
12039                scroll: this.ddScroll
12040            });
12041         }
12042         this.getSelectionModel().init(this);
12043         if (!this.root) {
12044             Roo.log("ROOT not set in tree");
12045             return this;
12046         }
12047         this.root.render();
12048         if(!this.rootVisible){
12049             this.root.renderChildren();
12050         }
12051         return this;
12052     }
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064
12065 /**
12066  * @class Roo.tree.DefaultSelectionModel
12067  * @extends Roo.util.Observable
12068  * The default single selection for a TreePanel.
12069  * @param {Object} cfg Configuration
12070  */
12071 Roo.tree.DefaultSelectionModel = function(cfg){
12072    this.selNode = null;
12073    
12074    
12075    
12076    this.addEvents({
12077        /**
12078         * @event selectionchange
12079         * Fires when the selected node changes
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         */
12083        "selectionchange" : true,
12084
12085        /**
12086         * @event beforeselect
12087         * Fires before the selected node changes, return false to cancel the change
12088         * @param {DefaultSelectionModel} this
12089         * @param {TreeNode} node the new selection
12090         * @param {TreeNode} node the old selection
12091         */
12092        "beforeselect" : true
12093    });
12094    
12095     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12096 };
12097
12098 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12099     init : function(tree){
12100         this.tree = tree;
12101         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12102         tree.on("click", this.onNodeClick, this);
12103     },
12104     
12105     onNodeClick : function(node, e){
12106         if (e.ctrlKey && this.selNode == node)  {
12107             this.unselect(node);
12108             return;
12109         }
12110         this.select(node);
12111     },
12112     
12113     /**
12114      * Select a node.
12115      * @param {TreeNode} node The node to select
12116      * @return {TreeNode} The selected node
12117      */
12118     select : function(node){
12119         var last = this.selNode;
12120         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12121             if(last){
12122                 last.ui.onSelectedChange(false);
12123             }
12124             this.selNode = node;
12125             node.ui.onSelectedChange(true);
12126             this.fireEvent("selectionchange", this, node, last);
12127         }
12128         return node;
12129     },
12130     
12131     /**
12132      * Deselect a node.
12133      * @param {TreeNode} node The node to unselect
12134      */
12135     unselect : function(node){
12136         if(this.selNode == node){
12137             this.clearSelections();
12138         }    
12139     },
12140     
12141     /**
12142      * Clear all selections
12143      */
12144     clearSelections : function(){
12145         var n = this.selNode;
12146         if(n){
12147             n.ui.onSelectedChange(false);
12148             this.selNode = null;
12149             this.fireEvent("selectionchange", this, null);
12150         }
12151         return n;
12152     },
12153     
12154     /**
12155      * Get the selected node
12156      * @return {TreeNode} The selected node
12157      */
12158     getSelectedNode : function(){
12159         return this.selNode;    
12160     },
12161     
12162     /**
12163      * Returns true if the node is selected
12164      * @param {TreeNode} node The node to check
12165      * @return {Boolean}
12166      */
12167     isSelected : function(node){
12168         return this.selNode == node;  
12169     },
12170
12171     /**
12172      * Selects the node above the selected node in the tree, intelligently walking the nodes
12173      * @return TreeNode The new selection
12174      */
12175     selectPrevious : function(){
12176         var s = this.selNode || this.lastSelNode;
12177         if(!s){
12178             return null;
12179         }
12180         var ps = s.previousSibling;
12181         if(ps){
12182             if(!ps.isExpanded() || ps.childNodes.length < 1){
12183                 return this.select(ps);
12184             } else{
12185                 var lc = ps.lastChild;
12186                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12187                     lc = lc.lastChild;
12188                 }
12189                 return this.select(lc);
12190             }
12191         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12192             return this.select(s.parentNode);
12193         }
12194         return null;
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectNext : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         if(s.firstChild && s.isExpanded()){
12207              return this.select(s.firstChild);
12208          }else if(s.nextSibling){
12209              return this.select(s.nextSibling);
12210          }else if(s.parentNode){
12211             var newS = null;
12212             s.parentNode.bubble(function(){
12213                 if(this.nextSibling){
12214                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12215                     return false;
12216                 }
12217             });
12218             return newS;
12219          }
12220         return null;
12221     },
12222
12223     onKeyDown : function(e){
12224         var s = this.selNode || this.lastSelNode;
12225         // undesirable, but required
12226         var sm = this;
12227         if(!s){
12228             return;
12229         }
12230         var k = e.getKey();
12231         switch(k){
12232              case e.DOWN:
12233                  e.stopEvent();
12234                  this.selectNext();
12235              break;
12236              case e.UP:
12237                  e.stopEvent();
12238                  this.selectPrevious();
12239              break;
12240              case e.RIGHT:
12241                  e.preventDefault();
12242                  if(s.hasChildNodes()){
12243                      if(!s.isExpanded()){
12244                          s.expand();
12245                      }else if(s.firstChild){
12246                          this.select(s.firstChild, e);
12247                      }
12248                  }
12249              break;
12250              case e.LEFT:
12251                  e.preventDefault();
12252                  if(s.hasChildNodes() && s.isExpanded()){
12253                      s.collapse();
12254                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12255                      this.select(s.parentNode, e);
12256                  }
12257              break;
12258         };
12259     }
12260 });
12261
12262 /**
12263  * @class Roo.tree.MultiSelectionModel
12264  * @extends Roo.util.Observable
12265  * Multi selection for a TreePanel.
12266  * @param {Object} cfg Configuration
12267  */
12268 Roo.tree.MultiSelectionModel = function(){
12269    this.selNodes = [];
12270    this.selMap = {};
12271    this.addEvents({
12272        /**
12273         * @event selectionchange
12274         * Fires when the selected nodes change
12275         * @param {MultiSelectionModel} this
12276         * @param {Array} nodes Array of the selected nodes
12277         */
12278        "selectionchange" : true
12279    });
12280    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12281    
12282 };
12283
12284 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12285     init : function(tree){
12286         this.tree = tree;
12287         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12288         tree.on("click", this.onNodeClick, this);
12289     },
12290     
12291     onNodeClick : function(node, e){
12292         this.select(node, e, e.ctrlKey);
12293     },
12294     
12295     /**
12296      * Select a node.
12297      * @param {TreeNode} node The node to select
12298      * @param {EventObject} e (optional) An event associated with the selection
12299      * @param {Boolean} keepExisting True to retain existing selections
12300      * @return {TreeNode} The selected node
12301      */
12302     select : function(node, e, keepExisting){
12303         if(keepExisting !== true){
12304             this.clearSelections(true);
12305         }
12306         if(this.isSelected(node)){
12307             this.lastSelNode = node;
12308             return node;
12309         }
12310         this.selNodes.push(node);
12311         this.selMap[node.id] = node;
12312         this.lastSelNode = node;
12313         node.ui.onSelectedChange(true);
12314         this.fireEvent("selectionchange", this, this.selNodes);
12315         return node;
12316     },
12317     
12318     /**
12319      * Deselect a node.
12320      * @param {TreeNode} node The node to unselect
12321      */
12322     unselect : function(node){
12323         if(this.selMap[node.id]){
12324             node.ui.onSelectedChange(false);
12325             var sn = this.selNodes;
12326             var index = -1;
12327             if(sn.indexOf){
12328                 index = sn.indexOf(node);
12329             }else{
12330                 for(var i = 0, len = sn.length; i < len; i++){
12331                     if(sn[i] == node){
12332                         index = i;
12333                         break;
12334                     }
12335                 }
12336             }
12337             if(index != -1){
12338                 this.selNodes.splice(index, 1);
12339             }
12340             delete this.selMap[node.id];
12341             this.fireEvent("selectionchange", this, this.selNodes);
12342         }
12343     },
12344     
12345     /**
12346      * Clear all selections
12347      */
12348     clearSelections : function(suppressEvent){
12349         var sn = this.selNodes;
12350         if(sn.length > 0){
12351             for(var i = 0, len = sn.length; i < len; i++){
12352                 sn[i].ui.onSelectedChange(false);
12353             }
12354             this.selNodes = [];
12355             this.selMap = {};
12356             if(suppressEvent !== true){
12357                 this.fireEvent("selectionchange", this, this.selNodes);
12358             }
12359         }
12360     },
12361     
12362     /**
12363      * Returns true if the node is selected
12364      * @param {TreeNode} node The node to check
12365      * @return {Boolean}
12366      */
12367     isSelected : function(node){
12368         return this.selMap[node.id] ? true : false;  
12369     },
12370     
12371     /**
12372      * Returns an array of the selected nodes
12373      * @return {Array}
12374      */
12375     getSelectedNodes : function(){
12376         return this.selNodes;    
12377     },
12378
12379     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12380
12381     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12382
12383     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12384 });/*
12385  * Based on:
12386  * Ext JS Library 1.1.1
12387  * Copyright(c) 2006-2007, Ext JS, LLC.
12388  *
12389  * Originally Released Under LGPL - original licence link has changed is not relivant.
12390  *
12391  * Fork - LGPL
12392  * <script type="text/javascript">
12393  */
12394  
12395 /**
12396  * @class Roo.tree.TreeNode
12397  * @extends Roo.data.Node
12398  * @cfg {String} text The text for this node
12399  * @cfg {Boolean} expanded true to start the node expanded
12400  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12401  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12402  * @cfg {Boolean} disabled true to start the node disabled
12403  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12404  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12405  * @cfg {String} cls A css class to be added to the node
12406  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12407  * @cfg {String} href URL of the link used for the node (defaults to #)
12408  * @cfg {String} hrefTarget target frame for the link
12409  * @cfg {String} qtip An Ext QuickTip for the node
12410  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12411  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12412  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12413  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12414  * (defaults to undefined with no checkbox rendered)
12415  * @constructor
12416  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12417  */
12418 Roo.tree.TreeNode = function(attributes){
12419     attributes = attributes || {};
12420     if(typeof attributes == "string"){
12421         attributes = {text: attributes};
12422     }
12423     this.childrenRendered = false;
12424     this.rendered = false;
12425     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12426     this.expanded = attributes.expanded === true;
12427     this.isTarget = attributes.isTarget !== false;
12428     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12429     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12430
12431     /**
12432      * Read-only. The text for this node. To change it use setText().
12433      * @type String
12434      */
12435     this.text = attributes.text;
12436     /**
12437      * True if this node is disabled.
12438      * @type Boolean
12439      */
12440     this.disabled = attributes.disabled === true;
12441
12442     this.addEvents({
12443         /**
12444         * @event textchange
12445         * Fires when the text for this node is changed
12446         * @param {Node} this This node
12447         * @param {String} text The new text
12448         * @param {String} oldText The old text
12449         */
12450         "textchange" : true,
12451         /**
12452         * @event beforeexpand
12453         * Fires before this node is expanded, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforeexpand" : true,
12459         /**
12460         * @event beforecollapse
12461         * Fires before this node is collapsed, return false to cancel.
12462         * @param {Node} this This node
12463         * @param {Boolean} deep
12464         * @param {Boolean} anim
12465         */
12466         "beforecollapse" : true,
12467         /**
12468         * @event expand
12469         * Fires when this node is expanded
12470         * @param {Node} this This node
12471         */
12472         "expand" : true,
12473         /**
12474         * @event disabledchange
12475         * Fires when the disabled status of this node changes
12476         * @param {Node} this This node
12477         * @param {Boolean} disabled
12478         */
12479         "disabledchange" : true,
12480         /**
12481         * @event collapse
12482         * Fires when this node is collapsed
12483         * @param {Node} this This node
12484         */
12485         "collapse" : true,
12486         /**
12487         * @event beforeclick
12488         * Fires before click processing. Return false to cancel the default action.
12489         * @param {Node} this This node
12490         * @param {Roo.EventObject} e The event object
12491         */
12492         "beforeclick":true,
12493         /**
12494         * @event checkchange
12495         * Fires when a node with a checkbox's checked property changes
12496         * @param {Node} this This node
12497         * @param {Boolean} checked
12498         */
12499         "checkchange":true,
12500         /**
12501         * @event click
12502         * Fires when this node is clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "click":true,
12507         /**
12508         * @event dblclick
12509         * Fires when this node is double clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "dblclick":true,
12514         /**
12515         * @event contextmenu
12516         * Fires when this node is right clicked
12517         * @param {Node} this This node
12518         * @param {Roo.EventObject} e The event object
12519         */
12520         "contextmenu":true,
12521         /**
12522         * @event beforechildrenrendered
12523         * Fires right before the child nodes for this node are rendered
12524         * @param {Node} this This node
12525         */
12526         "beforechildrenrendered":true
12527     });
12528
12529     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12530
12531     /**
12532      * Read-only. The UI for this node
12533      * @type TreeNodeUI
12534      */
12535     this.ui = new uiClass(this);
12536     
12537     // finally support items[]
12538     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12539         return;
12540     }
12541     
12542     
12543     Roo.each(this.attributes.items, function(c) {
12544         this.appendChild(Roo.factory(c,Roo.Tree));
12545     }, this);
12546     delete this.attributes.items;
12547     
12548     
12549     
12550 };
12551 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12552     preventHScroll: true,
12553     /**
12554      * Returns true if this node is expanded
12555      * @return {Boolean}
12556      */
12557     isExpanded : function(){
12558         return this.expanded;
12559     },
12560
12561     /**
12562      * Returns the UI object for this node
12563      * @return {TreeNodeUI}
12564      */
12565     getUI : function(){
12566         return this.ui;
12567     },
12568
12569     // private override
12570     setFirstChild : function(node){
12571         var of = this.firstChild;
12572         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12573         if(this.childrenRendered && of && node != of){
12574             of.renderIndent(true, true);
12575         }
12576         if(this.rendered){
12577             this.renderIndent(true, true);
12578         }
12579     },
12580
12581     // private override
12582     setLastChild : function(node){
12583         var ol = this.lastChild;
12584         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12585         if(this.childrenRendered && ol && node != ol){
12586             ol.renderIndent(true, true);
12587         }
12588         if(this.rendered){
12589             this.renderIndent(true, true);
12590         }
12591     },
12592
12593     // these methods are overridden to provide lazy rendering support
12594     // private override
12595     appendChild : function()
12596     {
12597         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12598         if(node && this.childrenRendered){
12599             node.render();
12600         }
12601         this.ui.updateExpandIcon();
12602         return node;
12603     },
12604
12605     // private override
12606     removeChild : function(node){
12607         this.ownerTree.getSelectionModel().unselect(node);
12608         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12609         // if it's been rendered remove dom node
12610         if(this.childrenRendered){
12611             node.ui.remove();
12612         }
12613         if(this.childNodes.length < 1){
12614             this.collapse(false, false);
12615         }else{
12616             this.ui.updateExpandIcon();
12617         }
12618         if(!this.firstChild) {
12619             this.childrenRendered = false;
12620         }
12621         return node;
12622     },
12623
12624     // private override
12625     insertBefore : function(node, refNode){
12626         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12627         if(newNode && refNode && this.childrenRendered){
12628             node.render();
12629         }
12630         this.ui.updateExpandIcon();
12631         return newNode;
12632     },
12633
12634     /**
12635      * Sets the text for this node
12636      * @param {String} text
12637      */
12638     setText : function(text){
12639         var oldText = this.text;
12640         this.text = text;
12641         this.attributes.text = text;
12642         if(this.rendered){ // event without subscribing
12643             this.ui.onTextChange(this, text, oldText);
12644         }
12645         this.fireEvent("textchange", this, text, oldText);
12646     },
12647
12648     /**
12649      * Triggers selection of this node
12650      */
12651     select : function(){
12652         this.getOwnerTree().getSelectionModel().select(this);
12653     },
12654
12655     /**
12656      * Triggers deselection of this node
12657      */
12658     unselect : function(){
12659         this.getOwnerTree().getSelectionModel().unselect(this);
12660     },
12661
12662     /**
12663      * Returns true if this node is selected
12664      * @return {Boolean}
12665      */
12666     isSelected : function(){
12667         return this.getOwnerTree().getSelectionModel().isSelected(this);
12668     },
12669
12670     /**
12671      * Expand this node.
12672      * @param {Boolean} deep (optional) True to expand all children as well
12673      * @param {Boolean} anim (optional) false to cancel the default animation
12674      * @param {Function} callback (optional) A callback to be called when
12675      * expanding this node completes (does not wait for deep expand to complete).
12676      * Called with 1 parameter, this node.
12677      */
12678     expand : function(deep, anim, callback){
12679         if(!this.expanded){
12680             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12681                 return;
12682             }
12683             if(!this.childrenRendered){
12684                 this.renderChildren();
12685             }
12686             this.expanded = true;
12687             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12688                 this.ui.animExpand(function(){
12689                     this.fireEvent("expand", this);
12690                     if(typeof callback == "function"){
12691                         callback(this);
12692                     }
12693                     if(deep === true){
12694                         this.expandChildNodes(true);
12695                     }
12696                 }.createDelegate(this));
12697                 return;
12698             }else{
12699                 this.ui.expand();
12700                 this.fireEvent("expand", this);
12701                 if(typeof callback == "function"){
12702                     callback(this);
12703                 }
12704             }
12705         }else{
12706            if(typeof callback == "function"){
12707                callback(this);
12708            }
12709         }
12710         if(deep === true){
12711             this.expandChildNodes(true);
12712         }
12713     },
12714
12715     isHiddenRoot : function(){
12716         return this.isRoot && !this.getOwnerTree().rootVisible;
12717     },
12718
12719     /**
12720      * Collapse this node.
12721      * @param {Boolean} deep (optional) True to collapse all children as well
12722      * @param {Boolean} anim (optional) false to cancel the default animation
12723      */
12724     collapse : function(deep, anim){
12725         if(this.expanded && !this.isHiddenRoot()){
12726             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12727                 return;
12728             }
12729             this.expanded = false;
12730             if((this.getOwnerTree().animate && anim !== false) || anim){
12731                 this.ui.animCollapse(function(){
12732                     this.fireEvent("collapse", this);
12733                     if(deep === true){
12734                         this.collapseChildNodes(true);
12735                     }
12736                 }.createDelegate(this));
12737                 return;
12738             }else{
12739                 this.ui.collapse();
12740                 this.fireEvent("collapse", this);
12741             }
12742         }
12743         if(deep === true){
12744             var cs = this.childNodes;
12745             for(var i = 0, len = cs.length; i < len; i++) {
12746                 cs[i].collapse(true, false);
12747             }
12748         }
12749     },
12750
12751     // private
12752     delayedExpand : function(delay){
12753         if(!this.expandProcId){
12754             this.expandProcId = this.expand.defer(delay, this);
12755         }
12756     },
12757
12758     // private
12759     cancelExpand : function(){
12760         if(this.expandProcId){
12761             clearTimeout(this.expandProcId);
12762         }
12763         this.expandProcId = false;
12764     },
12765
12766     /**
12767      * Toggles expanded/collapsed state of the node
12768      */
12769     toggle : function(){
12770         if(this.expanded){
12771             this.collapse();
12772         }else{
12773             this.expand();
12774         }
12775     },
12776
12777     /**
12778      * Ensures all parent nodes are expanded
12779      */
12780     ensureVisible : function(callback){
12781         var tree = this.getOwnerTree();
12782         tree.expandPath(this.parentNode.getPath(), false, function(){
12783             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12784             Roo.callback(callback);
12785         }.createDelegate(this));
12786     },
12787
12788     /**
12789      * Expand all child nodes
12790      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12791      */
12792     expandChildNodes : function(deep){
12793         var cs = this.childNodes;
12794         for(var i = 0, len = cs.length; i < len; i++) {
12795                 cs[i].expand(deep);
12796         }
12797     },
12798
12799     /**
12800      * Collapse all child nodes
12801      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12802      */
12803     collapseChildNodes : function(deep){
12804         var cs = this.childNodes;
12805         for(var i = 0, len = cs.length; i < len; i++) {
12806                 cs[i].collapse(deep);
12807         }
12808     },
12809
12810     /**
12811      * Disables this node
12812      */
12813     disable : function(){
12814         this.disabled = true;
12815         this.unselect();
12816         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12817             this.ui.onDisableChange(this, true);
12818         }
12819         this.fireEvent("disabledchange", this, true);
12820     },
12821
12822     /**
12823      * Enables this node
12824      */
12825     enable : function(){
12826         this.disabled = false;
12827         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12828             this.ui.onDisableChange(this, false);
12829         }
12830         this.fireEvent("disabledchange", this, false);
12831     },
12832
12833     // private
12834     renderChildren : function(suppressEvent){
12835         if(suppressEvent !== false){
12836             this.fireEvent("beforechildrenrendered", this);
12837         }
12838         var cs = this.childNodes;
12839         for(var i = 0, len = cs.length; i < len; i++){
12840             cs[i].render(true);
12841         }
12842         this.childrenRendered = true;
12843     },
12844
12845     // private
12846     sort : function(fn, scope){
12847         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12848         if(this.childrenRendered){
12849             var cs = this.childNodes;
12850             for(var i = 0, len = cs.length; i < len; i++){
12851                 cs[i].render(true);
12852             }
12853         }
12854     },
12855
12856     // private
12857     render : function(bulkRender){
12858         this.ui.render(bulkRender);
12859         if(!this.rendered){
12860             this.rendered = true;
12861             if(this.expanded){
12862                 this.expanded = false;
12863                 this.expand(false, false);
12864             }
12865         }
12866     },
12867
12868     // private
12869     renderIndent : function(deep, refresh){
12870         if(refresh){
12871             this.ui.childIndent = null;
12872         }
12873         this.ui.renderIndent();
12874         if(deep === true && this.childrenRendered){
12875             var cs = this.childNodes;
12876             for(var i = 0, len = cs.length; i < len; i++){
12877                 cs[i].renderIndent(true, refresh);
12878             }
12879         }
12880     }
12881 });/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891  
12892 /**
12893  * @class Roo.tree.AsyncTreeNode
12894  * @extends Roo.tree.TreeNode
12895  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12896  * @constructor
12897  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12898  */
12899  Roo.tree.AsyncTreeNode = function(config){
12900     this.loaded = false;
12901     this.loading = false;
12902     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12903     /**
12904     * @event beforeload
12905     * Fires before this node is loaded, return false to cancel
12906     * @param {Node} this This node
12907     */
12908     this.addEvents({'beforeload':true, 'load': true});
12909     /**
12910     * @event load
12911     * Fires when this node is loaded
12912     * @param {Node} this This node
12913     */
12914     /**
12915      * The loader used by this node (defaults to using the tree's defined loader)
12916      * @type TreeLoader
12917      * @property loader
12918      */
12919 };
12920 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12921     expand : function(deep, anim, callback){
12922         if(this.loading){ // if an async load is already running, waiting til it's done
12923             var timer;
12924             var f = function(){
12925                 if(!this.loading){ // done loading
12926                     clearInterval(timer);
12927                     this.expand(deep, anim, callback);
12928                 }
12929             }.createDelegate(this);
12930             timer = setInterval(f, 200);
12931             return;
12932         }
12933         if(!this.loaded){
12934             if(this.fireEvent("beforeload", this) === false){
12935                 return;
12936             }
12937             this.loading = true;
12938             this.ui.beforeLoad(this);
12939             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12940             if(loader){
12941                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12942                 return;
12943             }
12944         }
12945         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12946     },
12947     
12948     /**
12949      * Returns true if this node is currently loading
12950      * @return {Boolean}
12951      */
12952     isLoading : function(){
12953         return this.loading;  
12954     },
12955     
12956     loadComplete : function(deep, anim, callback){
12957         this.loading = false;
12958         this.loaded = true;
12959         this.ui.afterLoad(this);
12960         this.fireEvent("load", this);
12961         this.expand(deep, anim, callback);
12962     },
12963     
12964     /**
12965      * Returns true if this node has been loaded
12966      * @return {Boolean}
12967      */
12968     isLoaded : function(){
12969         return this.loaded;
12970     },
12971     
12972     hasChildNodes : function(){
12973         if(!this.isLeaf() && !this.loaded){
12974             return true;
12975         }else{
12976             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12977         }
12978     },
12979
12980     /**
12981      * Trigger a reload for this node
12982      * @param {Function} callback
12983      */
12984     reload : function(callback){
12985         this.collapse(false, false);
12986         while(this.firstChild){
12987             this.removeChild(this.firstChild);
12988         }
12989         this.childrenRendered = false;
12990         this.loaded = false;
12991         if(this.isHiddenRoot()){
12992             this.expanded = false;
12993         }
12994         this.expand(false, false, callback);
12995     }
12996 });/*
12997  * Based on:
12998  * Ext JS Library 1.1.1
12999  * Copyright(c) 2006-2007, Ext JS, LLC.
13000  *
13001  * Originally Released Under LGPL - original licence link has changed is not relivant.
13002  *
13003  * Fork - LGPL
13004  * <script type="text/javascript">
13005  */
13006  
13007 /**
13008  * @class Roo.tree.TreeNodeUI
13009  * @constructor
13010  * @param {Object} node The node to render
13011  * The TreeNode UI implementation is separate from the
13012  * tree implementation. Unless you are customizing the tree UI,
13013  * you should never have to use this directly.
13014  */
13015 Roo.tree.TreeNodeUI = function(node){
13016     this.node = node;
13017     this.rendered = false;
13018     this.animating = false;
13019     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13020 };
13021
13022 Roo.tree.TreeNodeUI.prototype = {
13023     removeChild : function(node){
13024         if(this.rendered){
13025             this.ctNode.removeChild(node.ui.getEl());
13026         }
13027     },
13028
13029     beforeLoad : function(){
13030          this.addClass("x-tree-node-loading");
13031     },
13032
13033     afterLoad : function(){
13034          this.removeClass("x-tree-node-loading");
13035     },
13036
13037     onTextChange : function(node, text, oldText){
13038         if(this.rendered){
13039             this.textNode.innerHTML = text;
13040         }
13041     },
13042
13043     onDisableChange : function(node, state){
13044         this.disabled = state;
13045         if(state){
13046             this.addClass("x-tree-node-disabled");
13047         }else{
13048             this.removeClass("x-tree-node-disabled");
13049         }
13050     },
13051
13052     onSelectedChange : function(state){
13053         if(state){
13054             this.focus();
13055             this.addClass("x-tree-selected");
13056         }else{
13057             //this.blur();
13058             this.removeClass("x-tree-selected");
13059         }
13060     },
13061
13062     onMove : function(tree, node, oldParent, newParent, index, refNode){
13063         this.childIndent = null;
13064         if(this.rendered){
13065             var targetNode = newParent.ui.getContainer();
13066             if(!targetNode){//target not rendered
13067                 this.holder = document.createElement("div");
13068                 this.holder.appendChild(this.wrap);
13069                 return;
13070             }
13071             var insertBefore = refNode ? refNode.ui.getEl() : null;
13072             if(insertBefore){
13073                 targetNode.insertBefore(this.wrap, insertBefore);
13074             }else{
13075                 targetNode.appendChild(this.wrap);
13076             }
13077             this.node.renderIndent(true);
13078         }
13079     },
13080
13081     addClass : function(cls){
13082         if(this.elNode){
13083             Roo.fly(this.elNode).addClass(cls);
13084         }
13085     },
13086
13087     removeClass : function(cls){
13088         if(this.elNode){
13089             Roo.fly(this.elNode).removeClass(cls);
13090         }
13091     },
13092
13093     remove : function(){
13094         if(this.rendered){
13095             this.holder = document.createElement("div");
13096             this.holder.appendChild(this.wrap);
13097         }
13098     },
13099
13100     fireEvent : function(){
13101         return this.node.fireEvent.apply(this.node, arguments);
13102     },
13103
13104     initEvents : function(){
13105         this.node.on("move", this.onMove, this);
13106         var E = Roo.EventManager;
13107         var a = this.anchor;
13108
13109         var el = Roo.fly(a, '_treeui');
13110
13111         if(Roo.isOpera){ // opera render bug ignores the CSS
13112             el.setStyle("text-decoration", "none");
13113         }
13114
13115         el.on("click", this.onClick, this);
13116         el.on("dblclick", this.onDblClick, this);
13117
13118         if(this.checkbox){
13119             Roo.EventManager.on(this.checkbox,
13120                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13121         }
13122
13123         el.on("contextmenu", this.onContextMenu, this);
13124
13125         var icon = Roo.fly(this.iconNode);
13126         icon.on("click", this.onClick, this);
13127         icon.on("dblclick", this.onDblClick, this);
13128         icon.on("contextmenu", this.onContextMenu, this);
13129         E.on(this.ecNode, "click", this.ecClick, this, true);
13130
13131         if(this.node.disabled){
13132             this.addClass("x-tree-node-disabled");
13133         }
13134         if(this.node.hidden){
13135             this.addClass("x-tree-node-disabled");
13136         }
13137         var ot = this.node.getOwnerTree();
13138         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13139         if(dd && (!this.node.isRoot || ot.rootVisible)){
13140             Roo.dd.Registry.register(this.elNode, {
13141                 node: this.node,
13142                 handles: this.getDDHandles(),
13143                 isHandle: false
13144             });
13145         }
13146     },
13147
13148     getDDHandles : function(){
13149         return [this.iconNode, this.textNode];
13150     },
13151
13152     hide : function(){
13153         if(this.rendered){
13154             this.wrap.style.display = "none";
13155         }
13156     },
13157
13158     show : function(){
13159         if(this.rendered){
13160             this.wrap.style.display = "";
13161         }
13162     },
13163
13164     onContextMenu : function(e){
13165         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13166             e.preventDefault();
13167             this.focus();
13168             this.fireEvent("contextmenu", this.node, e);
13169         }
13170     },
13171
13172     onClick : function(e){
13173         if(this.dropping){
13174             e.stopEvent();
13175             return;
13176         }
13177         if(this.fireEvent("beforeclick", this.node, e) !== false){
13178             if(!this.disabled && this.node.attributes.href){
13179                 this.fireEvent("click", this.node, e);
13180                 return;
13181             }
13182             e.preventDefault();
13183             if(this.disabled){
13184                 return;
13185             }
13186
13187             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13188                 this.node.toggle();
13189             }
13190
13191             this.fireEvent("click", this.node, e);
13192         }else{
13193             e.stopEvent();
13194         }
13195     },
13196
13197     onDblClick : function(e){
13198         e.preventDefault();
13199         if(this.disabled){
13200             return;
13201         }
13202         if(this.checkbox){
13203             this.toggleCheck();
13204         }
13205         if(!this.animating && this.node.hasChildNodes()){
13206             this.node.toggle();
13207         }
13208         this.fireEvent("dblclick", this.node, e);
13209     },
13210
13211     onCheckChange : function(){
13212         var checked = this.checkbox.checked;
13213         this.node.attributes.checked = checked;
13214         this.fireEvent('checkchange', this.node, checked);
13215     },
13216
13217     ecClick : function(e){
13218         if(!this.animating && this.node.hasChildNodes()){
13219             this.node.toggle();
13220         }
13221     },
13222
13223     startDrop : function(){
13224         this.dropping = true;
13225     },
13226
13227     // delayed drop so the click event doesn't get fired on a drop
13228     endDrop : function(){
13229        setTimeout(function(){
13230            this.dropping = false;
13231        }.createDelegate(this), 50);
13232     },
13233
13234     expand : function(){
13235         this.updateExpandIcon();
13236         this.ctNode.style.display = "";
13237     },
13238
13239     focus : function(){
13240         if(!this.node.preventHScroll){
13241             try{this.anchor.focus();
13242             }catch(e){}
13243         }else if(!Roo.isIE){
13244             try{
13245                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13246                 var l = noscroll.scrollLeft;
13247                 this.anchor.focus();
13248                 noscroll.scrollLeft = l;
13249             }catch(e){}
13250         }
13251     },
13252
13253     toggleCheck : function(value){
13254         var cb = this.checkbox;
13255         if(cb){
13256             cb.checked = (value === undefined ? !cb.checked : value);
13257         }
13258     },
13259
13260     blur : function(){
13261         try{
13262             this.anchor.blur();
13263         }catch(e){}
13264     },
13265
13266     animExpand : function(callback){
13267         var ct = Roo.get(this.ctNode);
13268         ct.stopFx();
13269         if(!this.node.hasChildNodes()){
13270             this.updateExpandIcon();
13271             this.ctNode.style.display = "";
13272             Roo.callback(callback);
13273             return;
13274         }
13275         this.animating = true;
13276         this.updateExpandIcon();
13277
13278         ct.slideIn('t', {
13279            callback : function(){
13280                this.animating = false;
13281                Roo.callback(callback);
13282             },
13283             scope: this,
13284             duration: this.node.ownerTree.duration || .25
13285         });
13286     },
13287
13288     highlight : function(){
13289         var tree = this.node.getOwnerTree();
13290         Roo.fly(this.wrap).highlight(
13291             tree.hlColor || "C3DAF9",
13292             {endColor: tree.hlBaseColor}
13293         );
13294     },
13295
13296     collapse : function(){
13297         this.updateExpandIcon();
13298         this.ctNode.style.display = "none";
13299     },
13300
13301     animCollapse : function(callback){
13302         var ct = Roo.get(this.ctNode);
13303         ct.enableDisplayMode('block');
13304         ct.stopFx();
13305
13306         this.animating = true;
13307         this.updateExpandIcon();
13308
13309         ct.slideOut('t', {
13310             callback : function(){
13311                this.animating = false;
13312                Roo.callback(callback);
13313             },
13314             scope: this,
13315             duration: this.node.ownerTree.duration || .25
13316         });
13317     },
13318
13319     getContainer : function(){
13320         return this.ctNode;
13321     },
13322
13323     getEl : function(){
13324         return this.wrap;
13325     },
13326
13327     appendDDGhost : function(ghostNode){
13328         ghostNode.appendChild(this.elNode.cloneNode(true));
13329     },
13330
13331     getDDRepairXY : function(){
13332         return Roo.lib.Dom.getXY(this.iconNode);
13333     },
13334
13335     onRender : function(){
13336         this.render();
13337     },
13338
13339     render : function(bulkRender){
13340         var n = this.node, a = n.attributes;
13341         var targetNode = n.parentNode ?
13342               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13343
13344         if(!this.rendered){
13345             this.rendered = true;
13346
13347             this.renderElements(n, a, targetNode, bulkRender);
13348
13349             if(a.qtip){
13350                if(this.textNode.setAttributeNS){
13351                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13352                    if(a.qtipTitle){
13353                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13354                    }
13355                }else{
13356                    this.textNode.setAttribute("ext:qtip", a.qtip);
13357                    if(a.qtipTitle){
13358                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13359                    }
13360                }
13361             }else if(a.qtipCfg){
13362                 a.qtipCfg.target = Roo.id(this.textNode);
13363                 Roo.QuickTips.register(a.qtipCfg);
13364             }
13365             this.initEvents();
13366             if(!this.node.expanded){
13367                 this.updateExpandIcon();
13368             }
13369         }else{
13370             if(bulkRender === true) {
13371                 targetNode.appendChild(this.wrap);
13372             }
13373         }
13374     },
13375
13376     renderElements : function(n, a, targetNode, bulkRender)
13377     {
13378         // add some indent caching, this helps performance when rendering a large tree
13379         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13380         var t = n.getOwnerTree();
13381         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13382         if (typeof(n.attributes.html) != 'undefined') {
13383             txt = n.attributes.html;
13384         }
13385         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13386         var cb = typeof a.checked == 'boolean';
13387         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13388         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13389             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13390             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13391             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13392             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13393             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13394              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13395                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13396             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13397             "</li>"];
13398
13399         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13400             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13401                                 n.nextSibling.ui.getEl(), buf.join(""));
13402         }else{
13403             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13404         }
13405
13406         this.elNode = this.wrap.childNodes[0];
13407         this.ctNode = this.wrap.childNodes[1];
13408         var cs = this.elNode.childNodes;
13409         this.indentNode = cs[0];
13410         this.ecNode = cs[1];
13411         this.iconNode = cs[2];
13412         var index = 3;
13413         if(cb){
13414             this.checkbox = cs[3];
13415             index++;
13416         }
13417         this.anchor = cs[index];
13418         this.textNode = cs[index].firstChild;
13419     },
13420
13421     getAnchor : function(){
13422         return this.anchor;
13423     },
13424
13425     getTextEl : function(){
13426         return this.textNode;
13427     },
13428
13429     getIconEl : function(){
13430         return this.iconNode;
13431     },
13432
13433     isChecked : function(){
13434         return this.checkbox ? this.checkbox.checked : false;
13435     },
13436
13437     updateExpandIcon : function(){
13438         if(this.rendered){
13439             var n = this.node, c1, c2;
13440             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13441             var hasChild = n.hasChildNodes();
13442             if(hasChild){
13443                 if(n.expanded){
13444                     cls += "-minus";
13445                     c1 = "x-tree-node-collapsed";
13446                     c2 = "x-tree-node-expanded";
13447                 }else{
13448                     cls += "-plus";
13449                     c1 = "x-tree-node-expanded";
13450                     c2 = "x-tree-node-collapsed";
13451                 }
13452                 if(this.wasLeaf){
13453                     this.removeClass("x-tree-node-leaf");
13454                     this.wasLeaf = false;
13455                 }
13456                 if(this.c1 != c1 || this.c2 != c2){
13457                     Roo.fly(this.elNode).replaceClass(c1, c2);
13458                     this.c1 = c1; this.c2 = c2;
13459                 }
13460             }else{
13461                 // this changes non-leafs into leafs if they have no children.
13462                 // it's not very rational behaviour..
13463                 
13464                 if(!this.wasLeaf && this.node.leaf){
13465                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13466                     delete this.c1;
13467                     delete this.c2;
13468                     this.wasLeaf = true;
13469                 }
13470             }
13471             var ecc = "x-tree-ec-icon "+cls;
13472             if(this.ecc != ecc){
13473                 this.ecNode.className = ecc;
13474                 this.ecc = ecc;
13475             }
13476         }
13477     },
13478
13479     getChildIndent : function(){
13480         if(!this.childIndent){
13481             var buf = [];
13482             var p = this.node;
13483             while(p){
13484                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13485                     if(!p.isLast()) {
13486                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13487                     } else {
13488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13489                     }
13490                 }
13491                 p = p.parentNode;
13492             }
13493             this.childIndent = buf.join("");
13494         }
13495         return this.childIndent;
13496     },
13497
13498     renderIndent : function(){
13499         if(this.rendered){
13500             var indent = "";
13501             var p = this.node.parentNode;
13502             if(p){
13503                 indent = p.ui.getChildIndent();
13504             }
13505             if(this.indentMarkup != indent){ // don't rerender if not required
13506                 this.indentNode.innerHTML = indent;
13507                 this.indentMarkup = indent;
13508             }
13509             this.updateExpandIcon();
13510         }
13511     }
13512 };
13513
13514 Roo.tree.RootTreeNodeUI = function(){
13515     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13516 };
13517 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13518     render : function(){
13519         if(!this.rendered){
13520             var targetNode = this.node.ownerTree.innerCt.dom;
13521             this.node.expanded = true;
13522             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13523             this.wrap = this.ctNode = targetNode.firstChild;
13524         }
13525     },
13526     collapse : function(){
13527     },
13528     expand : function(){
13529     }
13530 });/*
13531  * Based on:
13532  * Ext JS Library 1.1.1
13533  * Copyright(c) 2006-2007, Ext JS, LLC.
13534  *
13535  * Originally Released Under LGPL - original licence link has changed is not relivant.
13536  *
13537  * Fork - LGPL
13538  * <script type="text/javascript">
13539  */
13540 /**
13541  * @class Roo.tree.TreeLoader
13542  * @extends Roo.util.Observable
13543  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13544  * nodes from a specified URL. The response must be a javascript Array definition
13545  * who's elements are node definition objects. eg:
13546  * <pre><code>
13547 {  success : true,
13548    data :      [
13549    
13550     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13551     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13552     ]
13553 }
13554
13555
13556 </code></pre>
13557  * <br><br>
13558  * The old style respose with just an array is still supported, but not recommended.
13559  * <br><br>
13560  *
13561  * A server request is sent, and child nodes are loaded only when a node is expanded.
13562  * The loading node's id is passed to the server under the parameter name "node" to
13563  * enable the server to produce the correct child nodes.
13564  * <br><br>
13565  * To pass extra parameters, an event handler may be attached to the "beforeload"
13566  * event, and the parameters specified in the TreeLoader's baseParams property:
13567  * <pre><code>
13568     myTreeLoader.on("beforeload", function(treeLoader, node) {
13569         this.baseParams.category = node.attributes.category;
13570     }, this);
13571     
13572 </code></pre>
13573  *
13574  * This would pass an HTTP parameter called "category" to the server containing
13575  * the value of the Node's "category" attribute.
13576  * @constructor
13577  * Creates a new Treeloader.
13578  * @param {Object} config A config object containing config properties.
13579  */
13580 Roo.tree.TreeLoader = function(config){
13581     this.baseParams = {};
13582     this.requestMethod = "POST";
13583     Roo.apply(this, config);
13584
13585     this.addEvents({
13586     
13587         /**
13588          * @event beforeload
13589          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13590          * @param {Object} This TreeLoader object.
13591          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13592          * @param {Object} callback The callback function specified in the {@link #load} call.
13593          */
13594         beforeload : true,
13595         /**
13596          * @event load
13597          * Fires when the node has been successfuly loaded.
13598          * @param {Object} This TreeLoader object.
13599          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13600          * @param {Object} response The response object containing the data from the server.
13601          */
13602         load : true,
13603         /**
13604          * @event loadexception
13605          * Fires if the network request failed.
13606          * @param {Object} This TreeLoader object.
13607          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13608          * @param {Object} response The response object containing the data from the server.
13609          */
13610         loadexception : true,
13611         /**
13612          * @event create
13613          * Fires before a node is created, enabling you to return custom Node types 
13614          * @param {Object} This TreeLoader object.
13615          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13616          */
13617         create : true
13618     });
13619
13620     Roo.tree.TreeLoader.superclass.constructor.call(this);
13621 };
13622
13623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13624     /**
13625     * @cfg {String} dataUrl The URL from which to request a Json string which
13626     * specifies an array of node definition object representing the child nodes
13627     * to be loaded.
13628     */
13629     /**
13630     * @cfg {String} requestMethod either GET or POST
13631     * defaults to POST (due to BC)
13632     * to be loaded.
13633     */
13634     /**
13635     * @cfg {Object} baseParams (optional) An object containing properties which
13636     * specify HTTP parameters to be passed to each request for child nodes.
13637     */
13638     /**
13639     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13640     * created by this loader. If the attributes sent by the server have an attribute in this object,
13641     * they take priority.
13642     */
13643     /**
13644     * @cfg {Object} uiProviders (optional) An object containing properties which
13645     * 
13646     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13647     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13648     * <i>uiProvider</i> attribute of a returned child node is a string rather
13649     * than a reference to a TreeNodeUI implementation, this that string value
13650     * is used as a property name in the uiProviders object. You can define the provider named
13651     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13652     */
13653     uiProviders : {},
13654
13655     /**
13656     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13657     * child nodes before loading.
13658     */
13659     clearOnLoad : true,
13660
13661     /**
13662     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13663     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13664     * Grid query { data : [ .....] }
13665     */
13666     
13667     root : false,
13668      /**
13669     * @cfg {String} queryParam (optional) 
13670     * Name of the query as it will be passed on the querystring (defaults to 'node')
13671     * eg. the request will be ?node=[id]
13672     */
13673     
13674     
13675     queryParam: false,
13676     
13677     /**
13678      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13679      * This is called automatically when a node is expanded, but may be used to reload
13680      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13681      * @param {Roo.tree.TreeNode} node
13682      * @param {Function} callback
13683      */
13684     load : function(node, callback){
13685         if(this.clearOnLoad){
13686             while(node.firstChild){
13687                 node.removeChild(node.firstChild);
13688             }
13689         }
13690         if(node.attributes.children){ // preloaded json children
13691             var cs = node.attributes.children;
13692             for(var i = 0, len = cs.length; i < len; i++){
13693                 node.appendChild(this.createNode(cs[i]));
13694             }
13695             if(typeof callback == "function"){
13696                 callback();
13697             }
13698         }else if(this.dataUrl){
13699             this.requestData(node, callback);
13700         }
13701     },
13702
13703     getParams: function(node){
13704         var buf = [], bp = this.baseParams;
13705         for(var key in bp){
13706             if(typeof bp[key] != "function"){
13707                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13708             }
13709         }
13710         var n = this.queryParam === false ? 'node' : this.queryParam;
13711         buf.push(n + "=", encodeURIComponent(node.id));
13712         return buf.join("");
13713     },
13714
13715     requestData : function(node, callback){
13716         if(this.fireEvent("beforeload", this, node, callback) !== false){
13717             this.transId = Roo.Ajax.request({
13718                 method:this.requestMethod,
13719                 url: this.dataUrl||this.url,
13720                 success: this.handleResponse,
13721                 failure: this.handleFailure,
13722                 scope: this,
13723                 argument: {callback: callback, node: node},
13724                 params: this.getParams(node)
13725             });
13726         }else{
13727             // if the load is cancelled, make sure we notify
13728             // the node that we are done
13729             if(typeof callback == "function"){
13730                 callback();
13731             }
13732         }
13733     },
13734
13735     isLoading : function(){
13736         return this.transId ? true : false;
13737     },
13738
13739     abort : function(){
13740         if(this.isLoading()){
13741             Roo.Ajax.abort(this.transId);
13742         }
13743     },
13744
13745     // private
13746     createNode : function(attr)
13747     {
13748         // apply baseAttrs, nice idea Corey!
13749         if(this.baseAttrs){
13750             Roo.applyIf(attr, this.baseAttrs);
13751         }
13752         if(this.applyLoader !== false){
13753             attr.loader = this;
13754         }
13755         // uiProvider = depreciated..
13756         
13757         if(typeof(attr.uiProvider) == 'string'){
13758            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13759                 /**  eval:var:attr */ eval(attr.uiProvider);
13760         }
13761         if(typeof(this.uiProviders['default']) != 'undefined') {
13762             attr.uiProvider = this.uiProviders['default'];
13763         }
13764         
13765         this.fireEvent('create', this, attr);
13766         
13767         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13768         return(attr.leaf ?
13769                         new Roo.tree.TreeNode(attr) :
13770                         new Roo.tree.AsyncTreeNode(attr));
13771     },
13772
13773     processResponse : function(response, node, callback)
13774     {
13775         var json = response.responseText;
13776         try {
13777             
13778             var o = Roo.decode(json);
13779             
13780             if (this.root === false && typeof(o.success) != undefined) {
13781                 this.root = 'data'; // the default behaviour for list like data..
13782                 }
13783                 
13784             if (this.root !== false &&  !o.success) {
13785                 // it's a failure condition.
13786                 var a = response.argument;
13787                 this.fireEvent("loadexception", this, a.node, response);
13788                 Roo.log("Load failed - should have a handler really");
13789                 return;
13790             }
13791             
13792             
13793             
13794             if (this.root !== false) {
13795                  o = o[this.root];
13796             }
13797             
13798             for(var i = 0, len = o.length; i < len; i++){
13799                 var n = this.createNode(o[i]);
13800                 if(n){
13801                     node.appendChild(n);
13802                 }
13803             }
13804             if(typeof callback == "function"){
13805                 callback(this, node);
13806             }
13807         }catch(e){
13808             this.handleFailure(response);
13809         }
13810     },
13811
13812     handleResponse : function(response){
13813         this.transId = false;
13814         var a = response.argument;
13815         this.processResponse(response, a.node, a.callback);
13816         this.fireEvent("load", this, a.node, response);
13817     },
13818
13819     handleFailure : function(response)
13820     {
13821         // should handle failure better..
13822         this.transId = false;
13823         var a = response.argument;
13824         this.fireEvent("loadexception", this, a.node, response);
13825         if(typeof a.callback == "function"){
13826             a.callback(this, a.node);
13827         }
13828     }
13829 });/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840 /**
13841 * @class Roo.tree.TreeFilter
13842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13843 * @param {TreePanel} tree
13844 * @param {Object} config (optional)
13845  */
13846 Roo.tree.TreeFilter = function(tree, config){
13847     this.tree = tree;
13848     this.filtered = {};
13849     Roo.apply(this, config);
13850 };
13851
13852 Roo.tree.TreeFilter.prototype = {
13853     clearBlank:false,
13854     reverse:false,
13855     autoClear:false,
13856     remove:false,
13857
13858      /**
13859      * Filter the data by a specific attribute.
13860      * @param {String/RegExp} value Either string that the attribute value
13861      * should start with or a RegExp to test against the attribute
13862      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13863      * @param {TreeNode} startNode (optional) The node to start the filter at.
13864      */
13865     filter : function(value, attr, startNode){
13866         attr = attr || "text";
13867         var f;
13868         if(typeof value == "string"){
13869             var vlen = value.length;
13870             // auto clear empty filter
13871             if(vlen == 0 && this.clearBlank){
13872                 this.clear();
13873                 return;
13874             }
13875             value = value.toLowerCase();
13876             f = function(n){
13877                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13878             };
13879         }else if(value.exec){ // regex?
13880             f = function(n){
13881                 return value.test(n.attributes[attr]);
13882             };
13883         }else{
13884             throw 'Illegal filter type, must be string or regex';
13885         }
13886         this.filterBy(f, null, startNode);
13887         },
13888
13889     /**
13890      * Filter by a function. The passed function will be called with each
13891      * node in the tree (or from the startNode). If the function returns true, the node is kept
13892      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13893      * @param {Function} fn The filter function
13894      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13895      */
13896     filterBy : function(fn, scope, startNode){
13897         startNode = startNode || this.tree.root;
13898         if(this.autoClear){
13899             this.clear();
13900         }
13901         var af = this.filtered, rv = this.reverse;
13902         var f = function(n){
13903             if(n == startNode){
13904                 return true;
13905             }
13906             if(af[n.id]){
13907                 return false;
13908             }
13909             var m = fn.call(scope || n, n);
13910             if(!m || rv){
13911                 af[n.id] = n;
13912                 n.ui.hide();
13913                 return false;
13914             }
13915             return true;
13916         };
13917         startNode.cascade(f);
13918         if(this.remove){
13919            for(var id in af){
13920                if(typeof id != "function"){
13921                    var n = af[id];
13922                    if(n && n.parentNode){
13923                        n.parentNode.removeChild(n);
13924                    }
13925                }
13926            }
13927         }
13928     },
13929
13930     /**
13931      * Clears the current filter. Note: with the "remove" option
13932      * set a filter cannot be cleared.
13933      */
13934     clear : function(){
13935         var t = this.tree;
13936         var af = this.filtered;
13937         for(var id in af){
13938             if(typeof id != "function"){
13939                 var n = af[id];
13940                 if(n){
13941                     n.ui.show();
13942                 }
13943             }
13944         }
13945         this.filtered = {};
13946     }
13947 };
13948 /*
13949  * Based on:
13950  * Ext JS Library 1.1.1
13951  * Copyright(c) 2006-2007, Ext JS, LLC.
13952  *
13953  * Originally Released Under LGPL - original licence link has changed is not relivant.
13954  *
13955  * Fork - LGPL
13956  * <script type="text/javascript">
13957  */
13958  
13959
13960 /**
13961  * @class Roo.tree.TreeSorter
13962  * Provides sorting of nodes in a TreePanel
13963  * 
13964  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13965  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13966  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13967  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13968  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13969  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13970  * @constructor
13971  * @param {TreePanel} tree
13972  * @param {Object} config
13973  */
13974 Roo.tree.TreeSorter = function(tree, config){
13975     Roo.apply(this, config);
13976     tree.on("beforechildrenrendered", this.doSort, this);
13977     tree.on("append", this.updateSort, this);
13978     tree.on("insert", this.updateSort, this);
13979     
13980     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13981     var p = this.property || "text";
13982     var sortType = this.sortType;
13983     var fs = this.folderSort;
13984     var cs = this.caseSensitive === true;
13985     var leafAttr = this.leafAttr || 'leaf';
13986
13987     this.sortFn = function(n1, n2){
13988         if(fs){
13989             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13990                 return 1;
13991             }
13992             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13993                 return -1;
13994             }
13995         }
13996         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13997         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13998         if(v1 < v2){
13999                         return dsc ? +1 : -1;
14000                 }else if(v1 > v2){
14001                         return dsc ? -1 : +1;
14002         }else{
14003                 return 0;
14004         }
14005     };
14006 };
14007
14008 Roo.tree.TreeSorter.prototype = {
14009     doSort : function(node){
14010         node.sort(this.sortFn);
14011     },
14012     
14013     compareNodes : function(n1, n2){
14014         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14015     },
14016     
14017     updateSort : function(tree, node){
14018         if(node.childrenRendered){
14019             this.doSort.defer(1, this, [node]);
14020         }
14021     }
14022 };/*
14023  * Based on:
14024  * Ext JS Library 1.1.1
14025  * Copyright(c) 2006-2007, Ext JS, LLC.
14026  *
14027  * Originally Released Under LGPL - original licence link has changed is not relivant.
14028  *
14029  * Fork - LGPL
14030  * <script type="text/javascript">
14031  */
14032
14033 if(Roo.dd.DropZone){
14034     
14035 Roo.tree.TreeDropZone = function(tree, config){
14036     this.allowParentInsert = false;
14037     this.allowContainerDrop = false;
14038     this.appendOnly = false;
14039     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14040     this.tree = tree;
14041     this.lastInsertClass = "x-tree-no-status";
14042     this.dragOverData = {};
14043 };
14044
14045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14046     ddGroup : "TreeDD",
14047     scroll:  true,
14048     
14049     expandDelay : 1000,
14050     
14051     expandNode : function(node){
14052         if(node.hasChildNodes() && !node.isExpanded()){
14053             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14054         }
14055     },
14056     
14057     queueExpand : function(node){
14058         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14059     },
14060     
14061     cancelExpand : function(){
14062         if(this.expandProcId){
14063             clearTimeout(this.expandProcId);
14064             this.expandProcId = false;
14065         }
14066     },
14067     
14068     isValidDropPoint : function(n, pt, dd, e, data){
14069         if(!n || !data){ return false; }
14070         var targetNode = n.node;
14071         var dropNode = data.node;
14072         // default drop rules
14073         if(!(targetNode && targetNode.isTarget && pt)){
14074             return false;
14075         }
14076         if(pt == "append" && targetNode.allowChildren === false){
14077             return false;
14078         }
14079         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14080             return false;
14081         }
14082         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14083             return false;
14084         }
14085         // reuse the object
14086         var overEvent = this.dragOverData;
14087         overEvent.tree = this.tree;
14088         overEvent.target = targetNode;
14089         overEvent.data = data;
14090         overEvent.point = pt;
14091         overEvent.source = dd;
14092         overEvent.rawEvent = e;
14093         overEvent.dropNode = dropNode;
14094         overEvent.cancel = false;  
14095         var result = this.tree.fireEvent("nodedragover", overEvent);
14096         return overEvent.cancel === false && result !== false;
14097     },
14098     
14099     getDropPoint : function(e, n, dd)
14100     {
14101         var tn = n.node;
14102         if(tn.isRoot){
14103             return tn.allowChildren !== false ? "append" : false; // always append for root
14104         }
14105         var dragEl = n.ddel;
14106         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14107         var y = Roo.lib.Event.getPageY(e);
14108         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14109         
14110         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14111         var noAppend = tn.allowChildren === false;
14112         if(this.appendOnly || tn.parentNode.allowChildren === false){
14113             return noAppend ? false : "append";
14114         }
14115         var noBelow = false;
14116         if(!this.allowParentInsert){
14117             noBelow = tn.hasChildNodes() && tn.isExpanded();
14118         }
14119         var q = (b - t) / (noAppend ? 2 : 3);
14120         if(y >= t && y < (t + q)){
14121             return "above";
14122         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14123             return "below";
14124         }else{
14125             return "append";
14126         }
14127     },
14128     
14129     onNodeEnter : function(n, dd, e, data)
14130     {
14131         this.cancelExpand();
14132     },
14133     
14134     onNodeOver : function(n, dd, e, data)
14135     {
14136        
14137         var pt = this.getDropPoint(e, n, dd);
14138         var node = n.node;
14139         
14140         // auto node expand check
14141         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14142             this.queueExpand(node);
14143         }else if(pt != "append"){
14144             this.cancelExpand();
14145         }
14146         
14147         // set the insert point style on the target node
14148         var returnCls = this.dropNotAllowed;
14149         if(this.isValidDropPoint(n, pt, dd, e, data)){
14150            if(pt){
14151                var el = n.ddel;
14152                var cls;
14153                if(pt == "above"){
14154                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14155                    cls = "x-tree-drag-insert-above";
14156                }else if(pt == "below"){
14157                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14158                    cls = "x-tree-drag-insert-below";
14159                }else{
14160                    returnCls = "x-tree-drop-ok-append";
14161                    cls = "x-tree-drag-append";
14162                }
14163                if(this.lastInsertClass != cls){
14164                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14165                    this.lastInsertClass = cls;
14166                }
14167            }
14168        }
14169        return returnCls;
14170     },
14171     
14172     onNodeOut : function(n, dd, e, data){
14173         
14174         this.cancelExpand();
14175         this.removeDropIndicators(n);
14176     },
14177     
14178     onNodeDrop : function(n, dd, e, data){
14179         var point = this.getDropPoint(e, n, dd);
14180         var targetNode = n.node;
14181         targetNode.ui.startDrop();
14182         if(!this.isValidDropPoint(n, point, dd, e, data)){
14183             targetNode.ui.endDrop();
14184             return false;
14185         }
14186         // first try to find the drop node
14187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14188         var dropEvent = {
14189             tree : this.tree,
14190             target: targetNode,
14191             data: data,
14192             point: point,
14193             source: dd,
14194             rawEvent: e,
14195             dropNode: dropNode,
14196             cancel: !dropNode   
14197         };
14198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14200             targetNode.ui.endDrop();
14201             return false;
14202         }
14203         // allow target changing
14204         targetNode = dropEvent.target;
14205         if(point == "append" && !targetNode.isExpanded()){
14206             targetNode.expand(false, null, function(){
14207                 this.completeDrop(dropEvent);
14208             }.createDelegate(this));
14209         }else{
14210             this.completeDrop(dropEvent);
14211         }
14212         return true;
14213     },
14214     
14215     completeDrop : function(de){
14216         var ns = de.dropNode, p = de.point, t = de.target;
14217         if(!(ns instanceof Array)){
14218             ns = [ns];
14219         }
14220         var n;
14221         for(var i = 0, len = ns.length; i < len; i++){
14222             n = ns[i];
14223             if(p == "above"){
14224                 t.parentNode.insertBefore(n, t);
14225             }else if(p == "below"){
14226                 t.parentNode.insertBefore(n, t.nextSibling);
14227             }else{
14228                 t.appendChild(n);
14229             }
14230         }
14231         n.ui.focus();
14232         if(this.tree.hlDrop){
14233             n.ui.highlight();
14234         }
14235         t.ui.endDrop();
14236         this.tree.fireEvent("nodedrop", de);
14237     },
14238     
14239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14240         if(this.tree.hlDrop){
14241             dropNode.ui.focus();
14242             dropNode.ui.highlight();
14243         }
14244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14245     },
14246     
14247     getTree : function(){
14248         return this.tree;
14249     },
14250     
14251     removeDropIndicators : function(n){
14252         if(n && n.ddel){
14253             var el = n.ddel;
14254             Roo.fly(el).removeClass([
14255                     "x-tree-drag-insert-above",
14256                     "x-tree-drag-insert-below",
14257                     "x-tree-drag-append"]);
14258             this.lastInsertClass = "_noclass";
14259         }
14260     },
14261     
14262     beforeDragDrop : function(target, e, id){
14263         this.cancelExpand();
14264         return true;
14265     },
14266     
14267     afterRepair : function(data){
14268         if(data && Roo.enableFx){
14269             data.node.ui.highlight();
14270         }
14271         this.hideProxy();
14272     } 
14273     
14274 });
14275
14276 }
14277 /*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287  
14288
14289 if(Roo.dd.DragZone){
14290 Roo.tree.TreeDragZone = function(tree, config){
14291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14292     this.tree = tree;
14293 };
14294
14295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14296     ddGroup : "TreeDD",
14297    
14298     onBeforeDrag : function(data, e){
14299         var n = data.node;
14300         return n && n.draggable && !n.disabled;
14301     },
14302      
14303     
14304     onInitDrag : function(e){
14305         var data = this.dragData;
14306         this.tree.getSelectionModel().select(data.node);
14307         this.proxy.update("");
14308         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14309         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14310     },
14311     
14312     getRepairXY : function(e, data){
14313         return data.node.ui.getDDRepairXY();
14314     },
14315     
14316     onEndDrag : function(data, e){
14317         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14318         
14319         
14320     },
14321     
14322     onValidDrop : function(dd, e, id){
14323         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14324         this.hideProxy();
14325     },
14326     
14327     beforeInvalidDrop : function(e, id){
14328         // this scrolls the original position back into view
14329         var sm = this.tree.getSelectionModel();
14330         sm.clearSelections();
14331         sm.select(this.dragData.node);
14332     }
14333 });
14334 }/*
14335  * Based on:
14336  * Ext JS Library 1.1.1
14337  * Copyright(c) 2006-2007, Ext JS, LLC.
14338  *
14339  * Originally Released Under LGPL - original licence link has changed is not relivant.
14340  *
14341  * Fork - LGPL
14342  * <script type="text/javascript">
14343  */
14344 /**
14345  * @class Roo.tree.TreeEditor
14346  * @extends Roo.Editor
14347  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14348  * as the editor field.
14349  * @constructor
14350  * @param {Object} config (used to be the tree panel.)
14351  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14352  * 
14353  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14354  * @cfg {Roo.form.TextField|Object} field The field configuration
14355  *
14356  * 
14357  */
14358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14359     var tree = config;
14360     var field;
14361     if (oldconfig) { // old style..
14362         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14363     } else {
14364         // new style..
14365         tree = config.tree;
14366         config.field = config.field  || {};
14367         config.field.xtype = 'TextField';
14368         field = Roo.factory(config.field, Roo.form);
14369     }
14370     config = config || {};
14371     
14372     
14373     this.addEvents({
14374         /**
14375          * @event beforenodeedit
14376          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14377          * false from the handler of this event.
14378          * @param {Editor} this
14379          * @param {Roo.tree.Node} node 
14380          */
14381         "beforenodeedit" : true
14382     });
14383     
14384     //Roo.log(config);
14385     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14386
14387     this.tree = tree;
14388
14389     tree.on('beforeclick', this.beforeNodeClick, this);
14390     tree.getTreeEl().on('mousedown', this.hide, this);
14391     this.on('complete', this.updateNode, this);
14392     this.on('beforestartedit', this.fitToTree, this);
14393     this.on('startedit', this.bindScroll, this, {delay:10});
14394     this.on('specialkey', this.onSpecialKey, this);
14395 };
14396
14397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14398     /**
14399      * @cfg {String} alignment
14400      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14401      */
14402     alignment: "l-l",
14403     // inherit
14404     autoSize: false,
14405     /**
14406      * @cfg {Boolean} hideEl
14407      * True to hide the bound element while the editor is displayed (defaults to false)
14408      */
14409     hideEl : false,
14410     /**
14411      * @cfg {String} cls
14412      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14413      */
14414     cls: "x-small-editor x-tree-editor",
14415     /**
14416      * @cfg {Boolean} shim
14417      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14418      */
14419     shim:false,
14420     // inherit
14421     shadow:"frame",
14422     /**
14423      * @cfg {Number} maxWidth
14424      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14425      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14426      * scroll and client offsets into account prior to each edit.
14427      */
14428     maxWidth: 250,
14429
14430     editDelay : 350,
14431
14432     // private
14433     fitToTree : function(ed, el){
14434         var td = this.tree.getTreeEl().dom, nd = el.dom;
14435         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14436             td.scrollLeft = nd.offsetLeft;
14437         }
14438         var w = Math.min(
14439                 this.maxWidth,
14440                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14441         this.setSize(w, '');
14442         
14443         return this.fireEvent('beforenodeedit', this, this.editNode);
14444         
14445     },
14446
14447     // private
14448     triggerEdit : function(node){
14449         this.completeEdit();
14450         this.editNode = node;
14451         this.startEdit(node.ui.textNode, node.text);
14452     },
14453
14454     // private
14455     bindScroll : function(){
14456         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14457     },
14458
14459     // private
14460     beforeNodeClick : function(node, e){
14461         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14462         this.lastClick = new Date();
14463         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14464             e.stopEvent();
14465             this.triggerEdit(node);
14466             return false;
14467         }
14468         return true;
14469     },
14470
14471     // private
14472     updateNode : function(ed, value){
14473         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14474         this.editNode.setText(value);
14475     },
14476
14477     // private
14478     onHide : function(){
14479         Roo.tree.TreeEditor.superclass.onHide.call(this);
14480         if(this.editNode){
14481             this.editNode.ui.focus();
14482         }
14483     },
14484
14485     // private
14486     onSpecialKey : function(field, e){
14487         var k = e.getKey();
14488         if(k == e.ESC){
14489             e.stopEvent();
14490             this.cancelEdit();
14491         }else if(k == e.ENTER && !e.hasModifier()){
14492             e.stopEvent();
14493             this.completeEdit();
14494         }
14495     }
14496 });//<Script type="text/javascript">
14497 /*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507  
14508 /**
14509  * Not documented??? - probably should be...
14510  */
14511
14512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14513     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14514     
14515     renderElements : function(n, a, targetNode, bulkRender){
14516         //consel.log("renderElements?");
14517         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14518
14519         var t = n.getOwnerTree();
14520         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14521         
14522         var cols = t.columns;
14523         var bw = t.borderWidth;
14524         var c = cols[0];
14525         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14526          var cb = typeof a.checked == "boolean";
14527         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14528         var colcls = 'x-t-' + tid + '-c0';
14529         var buf = [
14530             '<li class="x-tree-node">',
14531             
14532                 
14533                 '<div class="x-tree-node-el ', a.cls,'">',
14534                     // extran...
14535                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14536                 
14537                 
14538                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14539                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14540                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14541                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14542                            (a.iconCls ? ' '+a.iconCls : ''),
14543                            '" unselectable="on" />',
14544                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14545                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14546                              
14547                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14548                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14549                             '<span unselectable="on" qtip="' + tx + '">',
14550                              tx,
14551                              '</span></a>' ,
14552                     '</div>',
14553                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14555                  ];
14556         for(var i = 1, len = cols.length; i < len; i++){
14557             c = cols[i];
14558             colcls = 'x-t-' + tid + '-c' +i;
14559             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14560             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14561                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14562                       "</div>");
14563          }
14564          
14565          buf.push(
14566             '</a>',
14567             '<div class="x-clear"></div></div>',
14568             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14569             "</li>");
14570         
14571         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14572             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14573                                 n.nextSibling.ui.getEl(), buf.join(""));
14574         }else{
14575             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14576         }
14577         var el = this.wrap.firstChild;
14578         this.elRow = el;
14579         this.elNode = el.firstChild;
14580         this.ranchor = el.childNodes[1];
14581         this.ctNode = this.wrap.childNodes[1];
14582         var cs = el.firstChild.childNodes;
14583         this.indentNode = cs[0];
14584         this.ecNode = cs[1];
14585         this.iconNode = cs[2];
14586         var index = 3;
14587         if(cb){
14588             this.checkbox = cs[3];
14589             index++;
14590         }
14591         this.anchor = cs[index];
14592         
14593         this.textNode = cs[index].firstChild;
14594         
14595         //el.on("click", this.onClick, this);
14596         //el.on("dblclick", this.onDblClick, this);
14597         
14598         
14599        // console.log(this);
14600     },
14601     initEvents : function(){
14602         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14603         
14604             
14605         var a = this.ranchor;
14606
14607         var el = Roo.get(a);
14608
14609         if(Roo.isOpera){ // opera render bug ignores the CSS
14610             el.setStyle("text-decoration", "none");
14611         }
14612
14613         el.on("click", this.onClick, this);
14614         el.on("dblclick", this.onDblClick, this);
14615         el.on("contextmenu", this.onContextMenu, this);
14616         
14617     },
14618     
14619     /*onSelectedChange : function(state){
14620         if(state){
14621             this.focus();
14622             this.addClass("x-tree-selected");
14623         }else{
14624             //this.blur();
14625             this.removeClass("x-tree-selected");
14626         }
14627     },*/
14628     addClass : function(cls){
14629         if(this.elRow){
14630             Roo.fly(this.elRow).addClass(cls);
14631         }
14632         
14633     },
14634     
14635     
14636     removeClass : function(cls){
14637         if(this.elRow){
14638             Roo.fly(this.elRow).removeClass(cls);
14639         }
14640     }
14641
14642     
14643     
14644 });//<Script type="text/javascript">
14645
14646 /*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656  
14657
14658 /**
14659  * @class Roo.tree.ColumnTree
14660  * @extends Roo.data.TreePanel
14661  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14662  * @cfg {int} borderWidth  compined right/left border allowance
14663  * @constructor
14664  * @param {String/HTMLElement/Element} el The container element
14665  * @param {Object} config
14666  */
14667 Roo.tree.ColumnTree =  function(el, config)
14668 {
14669    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14670    this.addEvents({
14671         /**
14672         * @event resize
14673         * Fire this event on a container when it resizes
14674         * @param {int} w Width
14675         * @param {int} h Height
14676         */
14677        "resize" : true
14678     });
14679     this.on('resize', this.onResize, this);
14680 };
14681
14682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14683     //lines:false,
14684     
14685     
14686     borderWidth: Roo.isBorderBox ? 0 : 2, 
14687     headEls : false,
14688     
14689     render : function(){
14690         // add the header.....
14691        
14692         Roo.tree.ColumnTree.superclass.render.apply(this);
14693         
14694         this.el.addClass('x-column-tree');
14695         
14696         this.headers = this.el.createChild(
14697             {cls:'x-tree-headers'},this.innerCt.dom);
14698    
14699         var cols = this.columns, c;
14700         var totalWidth = 0;
14701         this.headEls = [];
14702         var  len = cols.length;
14703         for(var i = 0; i < len; i++){
14704              c = cols[i];
14705              totalWidth += c.width;
14706             this.headEls.push(this.headers.createChild({
14707                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14708                  cn: {
14709                      cls:'x-tree-hd-text',
14710                      html: c.header
14711                  },
14712                  style:'width:'+(c.width-this.borderWidth)+'px;'
14713              }));
14714         }
14715         this.headers.createChild({cls:'x-clear'});
14716         // prevent floats from wrapping when clipped
14717         this.headers.setWidth(totalWidth);
14718         //this.innerCt.setWidth(totalWidth);
14719         this.innerCt.setStyle({ overflow: 'auto' });
14720         this.onResize(this.width, this.height);
14721              
14722         
14723     },
14724     onResize : function(w,h)
14725     {
14726         this.height = h;
14727         this.width = w;
14728         // resize cols..
14729         this.innerCt.setWidth(this.width);
14730         this.innerCt.setHeight(this.height-20);
14731         
14732         // headers...
14733         var cols = this.columns, c;
14734         var totalWidth = 0;
14735         var expEl = false;
14736         var len = cols.length;
14737         for(var i = 0; i < len; i++){
14738             c = cols[i];
14739             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14740                 // it's the expander..
14741                 expEl  = this.headEls[i];
14742                 continue;
14743             }
14744             totalWidth += c.width;
14745             
14746         }
14747         if (expEl) {
14748             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14749         }
14750         this.headers.setWidth(w-20);
14751
14752         
14753         
14754         
14755     }
14756 });
14757 /*
14758  * Based on:
14759  * Ext JS Library 1.1.1
14760  * Copyright(c) 2006-2007, Ext JS, LLC.
14761  *
14762  * Originally Released Under LGPL - original licence link has changed is not relivant.
14763  *
14764  * Fork - LGPL
14765  * <script type="text/javascript">
14766  */
14767  
14768 /**
14769  * @class Roo.menu.Menu
14770  * @extends Roo.util.Observable
14771  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14772  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14773  * @constructor
14774  * Creates a new Menu
14775  * @param {Object} config Configuration options
14776  */
14777 Roo.menu.Menu = function(config){
14778     Roo.apply(this, config);
14779     this.id = this.id || Roo.id();
14780     this.addEvents({
14781         /**
14782          * @event beforeshow
14783          * Fires before this menu is displayed
14784          * @param {Roo.menu.Menu} this
14785          */
14786         beforeshow : true,
14787         /**
14788          * @event beforehide
14789          * Fires before this menu is hidden
14790          * @param {Roo.menu.Menu} this
14791          */
14792         beforehide : true,
14793         /**
14794          * @event show
14795          * Fires after this menu is displayed
14796          * @param {Roo.menu.Menu} this
14797          */
14798         show : true,
14799         /**
14800          * @event hide
14801          * Fires after this menu is hidden
14802          * @param {Roo.menu.Menu} this
14803          */
14804         hide : true,
14805         /**
14806          * @event click
14807          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14808          * @param {Roo.menu.Menu} this
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          * @param {Roo.EventObject} e
14811          */
14812         click : true,
14813         /**
14814          * @event mouseover
14815          * Fires when the mouse is hovering over this menu
14816          * @param {Roo.menu.Menu} this
14817          * @param {Roo.EventObject} e
14818          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14819          */
14820         mouseover : true,
14821         /**
14822          * @event mouseout
14823          * Fires when the mouse exits this menu
14824          * @param {Roo.menu.Menu} this
14825          * @param {Roo.EventObject} e
14826          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14827          */
14828         mouseout : true,
14829         /**
14830          * @event itemclick
14831          * Fires when a menu item contained in this menu is clicked
14832          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14833          * @param {Roo.EventObject} e
14834          */
14835         itemclick: true
14836     });
14837     if (this.registerMenu) {
14838         Roo.menu.MenuMgr.register(this);
14839     }
14840     
14841     var mis = this.items;
14842     this.items = new Roo.util.MixedCollection();
14843     if(mis){
14844         this.add.apply(this, mis);
14845     }
14846 };
14847
14848 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14849     /**
14850      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14851      */
14852     minWidth : 120,
14853     /**
14854      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14855      * for bottom-right shadow (defaults to "sides")
14856      */
14857     shadow : "sides",
14858     /**
14859      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14860      * this menu (defaults to "tl-tr?")
14861      */
14862     subMenuAlign : "tl-tr?",
14863     /**
14864      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14865      * relative to its element of origin (defaults to "tl-bl?")
14866      */
14867     defaultAlign : "tl-bl?",
14868     /**
14869      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14870      */
14871     allowOtherMenus : false,
14872     /**
14873      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14874      */
14875     registerMenu : true,
14876
14877     hidden:true,
14878
14879     // private
14880     render : function(){
14881         if(this.el){
14882             return;
14883         }
14884         var el = this.el = new Roo.Layer({
14885             cls: "x-menu",
14886             shadow:this.shadow,
14887             constrain: false,
14888             parentEl: this.parentEl || document.body,
14889             zindex:15000
14890         });
14891
14892         this.keyNav = new Roo.menu.MenuNav(this);
14893
14894         if(this.plain){
14895             el.addClass("x-menu-plain");
14896         }
14897         if(this.cls){
14898             el.addClass(this.cls);
14899         }
14900         // generic focus element
14901         this.focusEl = el.createChild({
14902             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14903         });
14904         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14905         //disabling touch- as it's causing issues ..
14906         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14907         ul.on('click'   , this.onClick, this);
14908         
14909         
14910         ul.on("mouseover", this.onMouseOver, this);
14911         ul.on("mouseout", this.onMouseOut, this);
14912         this.items.each(function(item){
14913             if (item.hidden) {
14914                 return;
14915             }
14916             
14917             var li = document.createElement("li");
14918             li.className = "x-menu-list-item";
14919             ul.dom.appendChild(li);
14920             item.render(li, this);
14921         }, this);
14922         this.ul = ul;
14923         this.autoWidth();
14924     },
14925
14926     // private
14927     autoWidth : function(){
14928         var el = this.el, ul = this.ul;
14929         if(!el){
14930             return;
14931         }
14932         var w = this.width;
14933         if(w){
14934             el.setWidth(w);
14935         }else if(Roo.isIE){
14936             el.setWidth(this.minWidth);
14937             var t = el.dom.offsetWidth; // force recalc
14938             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14939         }
14940     },
14941
14942     // private
14943     delayAutoWidth : function(){
14944         if(this.rendered){
14945             if(!this.awTask){
14946                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14947             }
14948             this.awTask.delay(20);
14949         }
14950     },
14951
14952     // private
14953     findTargetItem : function(e){
14954         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14955         if(t && t.menuItemId){
14956             return this.items.get(t.menuItemId);
14957         }
14958     },
14959
14960     // private
14961     onClick : function(e){
14962         Roo.log("menu.onClick");
14963         var t = this.findTargetItem(e);
14964         if(!t){
14965             return;
14966         }
14967         Roo.log(e);
14968         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14969             if(t == this.activeItem && t.shouldDeactivate(e)){
14970                 this.activeItem.deactivate();
14971                 delete this.activeItem;
14972                 return;
14973             }
14974             if(t.canActivate){
14975                 this.setActiveItem(t, true);
14976             }
14977             return;
14978             
14979             
14980         }
14981         
14982         t.onClick(e);
14983         this.fireEvent("click", this, t, e);
14984     },
14985
14986     // private
14987     setActiveItem : function(item, autoExpand){
14988         if(item != this.activeItem){
14989             if(this.activeItem){
14990                 this.activeItem.deactivate();
14991             }
14992             this.activeItem = item;
14993             item.activate(autoExpand);
14994         }else if(autoExpand){
14995             item.expandMenu();
14996         }
14997     },
14998
14999     // private
15000     tryActivate : function(start, step){
15001         var items = this.items;
15002         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15003             var item = items.get(i);
15004             if(!item.disabled && item.canActivate){
15005                 this.setActiveItem(item, false);
15006                 return item;
15007             }
15008         }
15009         return false;
15010     },
15011
15012     // private
15013     onMouseOver : function(e){
15014         var t;
15015         if(t = this.findTargetItem(e)){
15016             if(t.canActivate && !t.disabled){
15017                 this.setActiveItem(t, true);
15018             }
15019         }
15020         this.fireEvent("mouseover", this, e, t);
15021     },
15022
15023     // private
15024     onMouseOut : function(e){
15025         var t;
15026         if(t = this.findTargetItem(e)){
15027             if(t == this.activeItem && t.shouldDeactivate(e)){
15028                 this.activeItem.deactivate();
15029                 delete this.activeItem;
15030             }
15031         }
15032         this.fireEvent("mouseout", this, e, t);
15033     },
15034
15035     /**
15036      * Read-only.  Returns true if the menu is currently displayed, else false.
15037      * @type Boolean
15038      */
15039     isVisible : function(){
15040         return this.el && !this.hidden;
15041     },
15042
15043     /**
15044      * Displays this menu relative to another element
15045      * @param {String/HTMLElement/Roo.Element} element The element to align to
15046      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15047      * the element (defaults to this.defaultAlign)
15048      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15049      */
15050     show : function(el, pos, parentMenu){
15051         this.parentMenu = parentMenu;
15052         if(!this.el){
15053             this.render();
15054         }
15055         this.fireEvent("beforeshow", this);
15056         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15057     },
15058
15059     /**
15060      * Displays this menu at a specific xy position
15061      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15062      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15063      */
15064     showAt : function(xy, parentMenu, /* private: */_e){
15065         this.parentMenu = parentMenu;
15066         if(!this.el){
15067             this.render();
15068         }
15069         if(_e !== false){
15070             this.fireEvent("beforeshow", this);
15071             xy = this.el.adjustForConstraints(xy);
15072         }
15073         this.el.setXY(xy);
15074         this.el.show();
15075         this.hidden = false;
15076         this.focus();
15077         this.fireEvent("show", this);
15078     },
15079
15080     focus : function(){
15081         if(!this.hidden){
15082             this.doFocus.defer(50, this);
15083         }
15084     },
15085
15086     doFocus : function(){
15087         if(!this.hidden){
15088             this.focusEl.focus();
15089         }
15090     },
15091
15092     /**
15093      * Hides this menu and optionally all parent menus
15094      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15095      */
15096     hide : function(deep){
15097         if(this.el && this.isVisible()){
15098             this.fireEvent("beforehide", this);
15099             if(this.activeItem){
15100                 this.activeItem.deactivate();
15101                 this.activeItem = null;
15102             }
15103             this.el.hide();
15104             this.hidden = true;
15105             this.fireEvent("hide", this);
15106         }
15107         if(deep === true && this.parentMenu){
15108             this.parentMenu.hide(true);
15109         }
15110     },
15111
15112     /**
15113      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15114      * Any of the following are valid:
15115      * <ul>
15116      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15117      * <li>An HTMLElement object which will be converted to a menu item</li>
15118      * <li>A menu item config object that will be created as a new menu item</li>
15119      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15120      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15121      * </ul>
15122      * Usage:
15123      * <pre><code>
15124 // Create the menu
15125 var menu = new Roo.menu.Menu();
15126
15127 // Create a menu item to add by reference
15128 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15129
15130 // Add a bunch of items at once using different methods.
15131 // Only the last item added will be returned.
15132 var item = menu.add(
15133     menuItem,                // add existing item by ref
15134     'Dynamic Item',          // new TextItem
15135     '-',                     // new separator
15136     { text: 'Config Item' }  // new item by config
15137 );
15138 </code></pre>
15139      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15140      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15141      */
15142     add : function(){
15143         var a = arguments, l = a.length, item;
15144         for(var i = 0; i < l; i++){
15145             var el = a[i];
15146             if ((typeof(el) == "object") && el.xtype && el.xns) {
15147                 el = Roo.factory(el, Roo.menu);
15148             }
15149             
15150             if(el.render){ // some kind of Item
15151                 item = this.addItem(el);
15152             }else if(typeof el == "string"){ // string
15153                 if(el == "separator" || el == "-"){
15154                     item = this.addSeparator();
15155                 }else{
15156                     item = this.addText(el);
15157                 }
15158             }else if(el.tagName || el.el){ // element
15159                 item = this.addElement(el);
15160             }else if(typeof el == "object"){ // must be menu item config?
15161                 item = this.addMenuItem(el);
15162             }
15163         }
15164         return item;
15165     },
15166
15167     /**
15168      * Returns this menu's underlying {@link Roo.Element} object
15169      * @return {Roo.Element} The element
15170      */
15171     getEl : function(){
15172         if(!this.el){
15173             this.render();
15174         }
15175         return this.el;
15176     },
15177
15178     /**
15179      * Adds a separator bar to the menu
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addSeparator : function(){
15183         return this.addItem(new Roo.menu.Separator());
15184     },
15185
15186     /**
15187      * Adds an {@link Roo.Element} object to the menu
15188      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addElement : function(el){
15192         return this.addItem(new Roo.menu.BaseItem(el));
15193     },
15194
15195     /**
15196      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15197      * @param {Roo.menu.Item} item The menu item to add
15198      * @return {Roo.menu.Item} The menu item that was added
15199      */
15200     addItem : function(item){
15201         this.items.add(item);
15202         if(this.ul){
15203             var li = document.createElement("li");
15204             li.className = "x-menu-list-item";
15205             this.ul.dom.appendChild(li);
15206             item.render(li, this);
15207             this.delayAutoWidth();
15208         }
15209         return item;
15210     },
15211
15212     /**
15213      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15214      * @param {Object} config A MenuItem config object
15215      * @return {Roo.menu.Item} The menu item that was added
15216      */
15217     addMenuItem : function(config){
15218         if(!(config instanceof Roo.menu.Item)){
15219             if(typeof config.checked == "boolean"){ // must be check menu item config?
15220                 config = new Roo.menu.CheckItem(config);
15221             }else{
15222                 config = new Roo.menu.Item(config);
15223             }
15224         }
15225         return this.addItem(config);
15226     },
15227
15228     /**
15229      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15230      * @param {String} text The text to display in the menu item
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     addText : function(text){
15234         return this.addItem(new Roo.menu.TextItem({ text : text }));
15235     },
15236
15237     /**
15238      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15239      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15240      * @param {Roo.menu.Item} item The menu item to add
15241      * @return {Roo.menu.Item} The menu item that was added
15242      */
15243     insert : function(index, item){
15244         this.items.insert(index, item);
15245         if(this.ul){
15246             var li = document.createElement("li");
15247             li.className = "x-menu-list-item";
15248             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15249             item.render(li, this);
15250             this.delayAutoWidth();
15251         }
15252         return item;
15253     },
15254
15255     /**
15256      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15257      * @param {Roo.menu.Item} item The menu item to remove
15258      */
15259     remove : function(item){
15260         this.items.removeKey(item.id);
15261         item.destroy();
15262     },
15263
15264     /**
15265      * Removes and destroys all items in the menu
15266      */
15267     removeAll : function(){
15268         var f;
15269         while(f = this.items.first()){
15270             this.remove(f);
15271         }
15272     }
15273 });
15274
15275 // MenuNav is a private utility class used internally by the Menu
15276 Roo.menu.MenuNav = function(menu){
15277     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15278     this.scope = this.menu = menu;
15279 };
15280
15281 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15282     doRelay : function(e, h){
15283         var k = e.getKey();
15284         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15285             this.menu.tryActivate(0, 1);
15286             return false;
15287         }
15288         return h.call(this.scope || this, e, this.menu);
15289     },
15290
15291     up : function(e, m){
15292         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15293             m.tryActivate(m.items.length-1, -1);
15294         }
15295     },
15296
15297     down : function(e, m){
15298         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15299             m.tryActivate(0, 1);
15300         }
15301     },
15302
15303     right : function(e, m){
15304         if(m.activeItem){
15305             m.activeItem.expandMenu(true);
15306         }
15307     },
15308
15309     left : function(e, m){
15310         m.hide();
15311         if(m.parentMenu && m.parentMenu.activeItem){
15312             m.parentMenu.activeItem.activate();
15313         }
15314     },
15315
15316     enter : function(e, m){
15317         if(m.activeItem){
15318             e.stopPropagation();
15319             m.activeItem.onClick(e);
15320             m.fireEvent("click", this, m.activeItem);
15321             return true;
15322         }
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334  
15335 /**
15336  * @class Roo.menu.MenuMgr
15337  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15338  * @singleton
15339  */
15340 Roo.menu.MenuMgr = function(){
15341    var menus, active, groups = {}, attached = false, lastShow = new Date();
15342
15343    // private - called when first menu is created
15344    function init(){
15345        menus = {};
15346        active = new Roo.util.MixedCollection();
15347        Roo.get(document).addKeyListener(27, function(){
15348            if(active.length > 0){
15349                hideAll();
15350            }
15351        });
15352    }
15353
15354    // private
15355    function hideAll(){
15356        if(active && active.length > 0){
15357            var c = active.clone();
15358            c.each(function(m){
15359                m.hide();
15360            });
15361        }
15362    }
15363
15364    // private
15365    function onHide(m){
15366        active.remove(m);
15367        if(active.length < 1){
15368            Roo.get(document).un("mousedown", onMouseDown);
15369            attached = false;
15370        }
15371    }
15372
15373    // private
15374    function onShow(m){
15375        var last = active.last();
15376        lastShow = new Date();
15377        active.add(m);
15378        if(!attached){
15379            Roo.get(document).on("mousedown", onMouseDown);
15380            attached = true;
15381        }
15382        if(m.parentMenu){
15383           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15384           m.parentMenu.activeChild = m;
15385        }else if(last && last.isVisible()){
15386           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15387        }
15388    }
15389
15390    // private
15391    function onBeforeHide(m){
15392        if(m.activeChild){
15393            m.activeChild.hide();
15394        }
15395        if(m.autoHideTimer){
15396            clearTimeout(m.autoHideTimer);
15397            delete m.autoHideTimer;
15398        }
15399    }
15400
15401    // private
15402    function onBeforeShow(m){
15403        var pm = m.parentMenu;
15404        if(!pm && !m.allowOtherMenus){
15405            hideAll();
15406        }else if(pm && pm.activeChild && active != m){
15407            pm.activeChild.hide();
15408        }
15409    }
15410
15411    // private
15412    function onMouseDown(e){
15413        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15414            hideAll();
15415        }
15416    }
15417
15418    // private
15419    function onBeforeCheck(mi, state){
15420        if(state){
15421            var g = groups[mi.group];
15422            for(var i = 0, l = g.length; i < l; i++){
15423                if(g[i] != mi){
15424                    g[i].setChecked(false);
15425                }
15426            }
15427        }
15428    }
15429
15430    return {
15431
15432        /**
15433         * Hides all menus that are currently visible
15434         */
15435        hideAll : function(){
15436             hideAll();  
15437        },
15438
15439        // private
15440        register : function(menu){
15441            if(!menus){
15442                init();
15443            }
15444            menus[menu.id] = menu;
15445            menu.on("beforehide", onBeforeHide);
15446            menu.on("hide", onHide);
15447            menu.on("beforeshow", onBeforeShow);
15448            menu.on("show", onShow);
15449            var g = menu.group;
15450            if(g && menu.events["checkchange"]){
15451                if(!groups[g]){
15452                    groups[g] = [];
15453                }
15454                groups[g].push(menu);
15455                menu.on("checkchange", onCheck);
15456            }
15457        },
15458
15459         /**
15460          * Returns a {@link Roo.menu.Menu} object
15461          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15462          * be used to generate and return a new Menu instance.
15463          */
15464        get : function(menu){
15465            if(typeof menu == "string"){ // menu id
15466                return menus[menu];
15467            }else if(menu.events){  // menu instance
15468                return menu;
15469            }else if(typeof menu.length == 'number'){ // array of menu items?
15470                return new Roo.menu.Menu({items:menu});
15471            }else{ // otherwise, must be a config
15472                return new Roo.menu.Menu(menu);
15473            }
15474        },
15475
15476        // private
15477        unregister : function(menu){
15478            delete menus[menu.id];
15479            menu.un("beforehide", onBeforeHide);
15480            menu.un("hide", onHide);
15481            menu.un("beforeshow", onBeforeShow);
15482            menu.un("show", onShow);
15483            var g = menu.group;
15484            if(g && menu.events["checkchange"]){
15485                groups[g].remove(menu);
15486                menu.un("checkchange", onCheck);
15487            }
15488        },
15489
15490        // private
15491        registerCheckable : function(menuItem){
15492            var g = menuItem.group;
15493            if(g){
15494                if(!groups[g]){
15495                    groups[g] = [];
15496                }
15497                groups[g].push(menuItem);
15498                menuItem.on("beforecheckchange", onBeforeCheck);
15499            }
15500        },
15501
15502        // private
15503        unregisterCheckable : function(menuItem){
15504            var g = menuItem.group;
15505            if(g){
15506                groups[g].remove(menuItem);
15507                menuItem.un("beforecheckchange", onBeforeCheck);
15508            }
15509        }
15510    };
15511 }();/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521  
15522
15523 /**
15524  * @class Roo.menu.BaseItem
15525  * @extends Roo.Component
15526  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15527  * management and base configuration options shared by all menu components.
15528  * @constructor
15529  * Creates a new BaseItem
15530  * @param {Object} config Configuration options
15531  */
15532 Roo.menu.BaseItem = function(config){
15533     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15534
15535     this.addEvents({
15536         /**
15537          * @event click
15538          * Fires when this item is clicked
15539          * @param {Roo.menu.BaseItem} this
15540          * @param {Roo.EventObject} e
15541          */
15542         click: true,
15543         /**
15544          * @event activate
15545          * Fires when this item is activated
15546          * @param {Roo.menu.BaseItem} this
15547          */
15548         activate : true,
15549         /**
15550          * @event deactivate
15551          * Fires when this item is deactivated
15552          * @param {Roo.menu.BaseItem} this
15553          */
15554         deactivate : true
15555     });
15556
15557     if(this.handler){
15558         this.on("click", this.handler, this.scope, true);
15559     }
15560 };
15561
15562 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15563     /**
15564      * @cfg {Function} handler
15565      * A function that will handle the click event of this menu item (defaults to undefined)
15566      */
15567     /**
15568      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15569      */
15570     canActivate : false,
15571     
15572      /**
15573      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15574      */
15575     hidden: false,
15576     
15577     /**
15578      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15579      */
15580     activeClass : "x-menu-item-active",
15581     /**
15582      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15583      */
15584     hideOnClick : true,
15585     /**
15586      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15587      */
15588     hideDelay : 100,
15589
15590     // private
15591     ctype: "Roo.menu.BaseItem",
15592
15593     // private
15594     actionMode : "container",
15595
15596     // private
15597     render : function(container, parentMenu){
15598         this.parentMenu = parentMenu;
15599         Roo.menu.BaseItem.superclass.render.call(this, container);
15600         this.container.menuItemId = this.id;
15601     },
15602
15603     // private
15604     onRender : function(container, position){
15605         this.el = Roo.get(this.el);
15606         container.dom.appendChild(this.el.dom);
15607     },
15608
15609     // private
15610     onClick : function(e){
15611         if(!this.disabled && this.fireEvent("click", this, e) !== false
15612                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15613             this.handleClick(e);
15614         }else{
15615             e.stopEvent();
15616         }
15617     },
15618
15619     // private
15620     activate : function(){
15621         if(this.disabled){
15622             return false;
15623         }
15624         var li = this.container;
15625         li.addClass(this.activeClass);
15626         this.region = li.getRegion().adjust(2, 2, -2, -2);
15627         this.fireEvent("activate", this);
15628         return true;
15629     },
15630
15631     // private
15632     deactivate : function(){
15633         this.container.removeClass(this.activeClass);
15634         this.fireEvent("deactivate", this);
15635     },
15636
15637     // private
15638     shouldDeactivate : function(e){
15639         return !this.region || !this.region.contains(e.getPoint());
15640     },
15641
15642     // private
15643     handleClick : function(e){
15644         if(this.hideOnClick){
15645             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15646         }
15647     },
15648
15649     // private
15650     expandMenu : function(autoActivate){
15651         // do nothing
15652     },
15653
15654     // private
15655     hideMenu : function(){
15656         // do nothing
15657     }
15658 });/*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668  
15669 /**
15670  * @class Roo.menu.Adapter
15671  * @extends Roo.menu.BaseItem
15672  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15673  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15674  * @constructor
15675  * Creates a new Adapter
15676  * @param {Object} config Configuration options
15677  */
15678 Roo.menu.Adapter = function(component, config){
15679     Roo.menu.Adapter.superclass.constructor.call(this, config);
15680     this.component = component;
15681 };
15682 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15683     // private
15684     canActivate : true,
15685
15686     // private
15687     onRender : function(container, position){
15688         this.component.render(container);
15689         this.el = this.component.getEl();
15690     },
15691
15692     // private
15693     activate : function(){
15694         if(this.disabled){
15695             return false;
15696         }
15697         this.component.focus();
15698         this.fireEvent("activate", this);
15699         return true;
15700     },
15701
15702     // private
15703     deactivate : function(){
15704         this.fireEvent("deactivate", this);
15705     },
15706
15707     // private
15708     disable : function(){
15709         this.component.disable();
15710         Roo.menu.Adapter.superclass.disable.call(this);
15711     },
15712
15713     // private
15714     enable : function(){
15715         this.component.enable();
15716         Roo.menu.Adapter.superclass.enable.call(this);
15717     }
15718 });/*
15719  * Based on:
15720  * Ext JS Library 1.1.1
15721  * Copyright(c) 2006-2007, Ext JS, LLC.
15722  *
15723  * Originally Released Under LGPL - original licence link has changed is not relivant.
15724  *
15725  * Fork - LGPL
15726  * <script type="text/javascript">
15727  */
15728
15729 /**
15730  * @class Roo.menu.TextItem
15731  * @extends Roo.menu.BaseItem
15732  * Adds a static text string to a menu, usually used as either a heading or group separator.
15733  * Note: old style constructor with text is still supported.
15734  * 
15735  * @constructor
15736  * Creates a new TextItem
15737  * @param {Object} cfg Configuration
15738  */
15739 Roo.menu.TextItem = function(cfg){
15740     if (typeof(cfg) == 'string') {
15741         this.text = cfg;
15742     } else {
15743         Roo.apply(this,cfg);
15744     }
15745     
15746     Roo.menu.TextItem.superclass.constructor.call(this);
15747 };
15748
15749 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15750     /**
15751      * @cfg {Boolean} text Text to show on item.
15752      */
15753     text : '',
15754     
15755     /**
15756      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15757      */
15758     hideOnClick : false,
15759     /**
15760      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15761      */
15762     itemCls : "x-menu-text",
15763
15764     // private
15765     onRender : function(){
15766         var s = document.createElement("span");
15767         s.className = this.itemCls;
15768         s.innerHTML = this.text;
15769         this.el = s;
15770         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15771     }
15772 });/*
15773  * Based on:
15774  * Ext JS Library 1.1.1
15775  * Copyright(c) 2006-2007, Ext JS, LLC.
15776  *
15777  * Originally Released Under LGPL - original licence link has changed is not relivant.
15778  *
15779  * Fork - LGPL
15780  * <script type="text/javascript">
15781  */
15782
15783 /**
15784  * @class Roo.menu.Separator
15785  * @extends Roo.menu.BaseItem
15786  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15787  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15788  * @constructor
15789  * @param {Object} config Configuration options
15790  */
15791 Roo.menu.Separator = function(config){
15792     Roo.menu.Separator.superclass.constructor.call(this, config);
15793 };
15794
15795 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15796     /**
15797      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15798      */
15799     itemCls : "x-menu-sep",
15800     /**
15801      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15802      */
15803     hideOnClick : false,
15804
15805     // private
15806     onRender : function(li){
15807         var s = document.createElement("span");
15808         s.className = this.itemCls;
15809         s.innerHTML = "&#160;";
15810         this.el = s;
15811         li.addClass("x-menu-sep-li");
15812         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15813     }
15814 });/*
15815  * Based on:
15816  * Ext JS Library 1.1.1
15817  * Copyright(c) 2006-2007, Ext JS, LLC.
15818  *
15819  * Originally Released Under LGPL - original licence link has changed is not relivant.
15820  *
15821  * Fork - LGPL
15822  * <script type="text/javascript">
15823  */
15824 /**
15825  * @class Roo.menu.Item
15826  * @extends Roo.menu.BaseItem
15827  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15828  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15829  * activation and click handling.
15830  * @constructor
15831  * Creates a new Item
15832  * @param {Object} config Configuration options
15833  */
15834 Roo.menu.Item = function(config){
15835     Roo.menu.Item.superclass.constructor.call(this, config);
15836     if(this.menu){
15837         this.menu = Roo.menu.MenuMgr.get(this.menu);
15838     }
15839 };
15840 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15841     
15842     /**
15843      * @cfg {String} text
15844      * The text to show on the menu item.
15845      */
15846     text: '',
15847      /**
15848      * @cfg {String} HTML to render in menu
15849      * The text to show on the menu item (HTML version).
15850      */
15851     html: '',
15852     /**
15853      * @cfg {String} icon
15854      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15855      */
15856     icon: undefined,
15857     /**
15858      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15859      */
15860     itemCls : "x-menu-item",
15861     /**
15862      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15863      */
15864     canActivate : true,
15865     /**
15866      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15867      */
15868     showDelay: 200,
15869     // doc'd in BaseItem
15870     hideDelay: 200,
15871
15872     // private
15873     ctype: "Roo.menu.Item",
15874     
15875     // private
15876     onRender : function(container, position){
15877         var el = document.createElement("a");
15878         el.hideFocus = true;
15879         el.unselectable = "on";
15880         el.href = this.href || "#";
15881         if(this.hrefTarget){
15882             el.target = this.hrefTarget;
15883         }
15884         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15885         
15886         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15887         
15888         el.innerHTML = String.format(
15889                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15890                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15891         this.el = el;
15892         Roo.menu.Item.superclass.onRender.call(this, container, position);
15893     },
15894
15895     /**
15896      * Sets the text to display in this menu item
15897      * @param {String} text The text to display
15898      * @param {Boolean} isHTML true to indicate text is pure html.
15899      */
15900     setText : function(text, isHTML){
15901         if (isHTML) {
15902             this.html = text;
15903         } else {
15904             this.text = text;
15905             this.html = '';
15906         }
15907         if(this.rendered){
15908             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15909      
15910             this.el.update(String.format(
15911                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15912                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15913             this.parentMenu.autoWidth();
15914         }
15915     },
15916
15917     // private
15918     handleClick : function(e){
15919         if(!this.href){ // if no link defined, stop the event automatically
15920             e.stopEvent();
15921         }
15922         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15923     },
15924
15925     // private
15926     activate : function(autoExpand){
15927         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15928             this.focus();
15929             if(autoExpand){
15930                 this.expandMenu();
15931             }
15932         }
15933         return true;
15934     },
15935
15936     // private
15937     shouldDeactivate : function(e){
15938         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15939             if(this.menu && this.menu.isVisible()){
15940                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15941             }
15942             return true;
15943         }
15944         return false;
15945     },
15946
15947     // private
15948     deactivate : function(){
15949         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15950         this.hideMenu();
15951     },
15952
15953     // private
15954     expandMenu : function(autoActivate){
15955         if(!this.disabled && this.menu){
15956             clearTimeout(this.hideTimer);
15957             delete this.hideTimer;
15958             if(!this.menu.isVisible() && !this.showTimer){
15959                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15960             }else if (this.menu.isVisible() && autoActivate){
15961                 this.menu.tryActivate(0, 1);
15962             }
15963         }
15964     },
15965
15966     // private
15967     deferExpand : function(autoActivate){
15968         delete this.showTimer;
15969         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15970         if(autoActivate){
15971             this.menu.tryActivate(0, 1);
15972         }
15973     },
15974
15975     // private
15976     hideMenu : function(){
15977         clearTimeout(this.showTimer);
15978         delete this.showTimer;
15979         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15980             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15981         }
15982     },
15983
15984     // private
15985     deferHide : function(){
15986         delete this.hideTimer;
15987         this.menu.hide();
15988     }
15989 });/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999  
16000 /**
16001  * @class Roo.menu.CheckItem
16002  * @extends Roo.menu.Item
16003  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16004  * @constructor
16005  * Creates a new CheckItem
16006  * @param {Object} config Configuration options
16007  */
16008 Roo.menu.CheckItem = function(config){
16009     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16010     this.addEvents({
16011         /**
16012          * @event beforecheckchange
16013          * Fires before the checked value is set, providing an opportunity to cancel if needed
16014          * @param {Roo.menu.CheckItem} this
16015          * @param {Boolean} checked The new checked value that will be set
16016          */
16017         "beforecheckchange" : true,
16018         /**
16019          * @event checkchange
16020          * Fires after the checked value has been set
16021          * @param {Roo.menu.CheckItem} this
16022          * @param {Boolean} checked The checked value that was set
16023          */
16024         "checkchange" : true
16025     });
16026     if(this.checkHandler){
16027         this.on('checkchange', this.checkHandler, this.scope);
16028     }
16029 };
16030 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16031     /**
16032      * @cfg {String} group
16033      * All check items with the same group name will automatically be grouped into a single-select
16034      * radio button group (defaults to '')
16035      */
16036     /**
16037      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16038      */
16039     itemCls : "x-menu-item x-menu-check-item",
16040     /**
16041      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16042      */
16043     groupClass : "x-menu-group-item",
16044
16045     /**
16046      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16047      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16048      * initialized with checked = true will be rendered as checked.
16049      */
16050     checked: false,
16051
16052     // private
16053     ctype: "Roo.menu.CheckItem",
16054
16055     // private
16056     onRender : function(c){
16057         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16058         if(this.group){
16059             this.el.addClass(this.groupClass);
16060         }
16061         Roo.menu.MenuMgr.registerCheckable(this);
16062         if(this.checked){
16063             this.checked = false;
16064             this.setChecked(true, true);
16065         }
16066     },
16067
16068     // private
16069     destroy : function(){
16070         if(this.rendered){
16071             Roo.menu.MenuMgr.unregisterCheckable(this);
16072         }
16073         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16074     },
16075
16076     /**
16077      * Set the checked state of this item
16078      * @param {Boolean} checked The new checked value
16079      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16080      */
16081     setChecked : function(state, suppressEvent){
16082         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16083             if(this.container){
16084                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16085             }
16086             this.checked = state;
16087             if(suppressEvent !== true){
16088                 this.fireEvent("checkchange", this, state);
16089             }
16090         }
16091     },
16092
16093     // private
16094     handleClick : function(e){
16095        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16096            this.setChecked(!this.checked);
16097        }
16098        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16099     }
16100 });/*
16101  * Based on:
16102  * Ext JS Library 1.1.1
16103  * Copyright(c) 2006-2007, Ext JS, LLC.
16104  *
16105  * Originally Released Under LGPL - original licence link has changed is not relivant.
16106  *
16107  * Fork - LGPL
16108  * <script type="text/javascript">
16109  */
16110  
16111 /**
16112  * @class Roo.menu.DateItem
16113  * @extends Roo.menu.Adapter
16114  * A menu item that wraps the {@link Roo.DatPicker} component.
16115  * @constructor
16116  * Creates a new DateItem
16117  * @param {Object} config Configuration options
16118  */
16119 Roo.menu.DateItem = function(config){
16120     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16121     /** The Roo.DatePicker object @type Roo.DatePicker */
16122     this.picker = this.component;
16123     this.addEvents({select: true});
16124     
16125     this.picker.on("render", function(picker){
16126         picker.getEl().swallowEvent("click");
16127         picker.container.addClass("x-menu-date-item");
16128     });
16129
16130     this.picker.on("select", this.onSelect, this);
16131 };
16132
16133 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16134     // private
16135     onSelect : function(picker, date){
16136         this.fireEvent("select", this, date, picker);
16137         Roo.menu.DateItem.superclass.handleClick.call(this);
16138     }
16139 });/*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149  
16150 /**
16151  * @class Roo.menu.ColorItem
16152  * @extends Roo.menu.Adapter
16153  * A menu item that wraps the {@link Roo.ColorPalette} component.
16154  * @constructor
16155  * Creates a new ColorItem
16156  * @param {Object} config Configuration options
16157  */
16158 Roo.menu.ColorItem = function(config){
16159     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16160     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16161     this.palette = this.component;
16162     this.relayEvents(this.palette, ["select"]);
16163     if(this.selectHandler){
16164         this.on('select', this.selectHandler, this.scope);
16165     }
16166 };
16167 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16168  * Based on:
16169  * Ext JS Library 1.1.1
16170  * Copyright(c) 2006-2007, Ext JS, LLC.
16171  *
16172  * Originally Released Under LGPL - original licence link has changed is not relivant.
16173  *
16174  * Fork - LGPL
16175  * <script type="text/javascript">
16176  */
16177  
16178
16179 /**
16180  * @class Roo.menu.DateMenu
16181  * @extends Roo.menu.Menu
16182  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16183  * @constructor
16184  * Creates a new DateMenu
16185  * @param {Object} config Configuration options
16186  */
16187 Roo.menu.DateMenu = function(config){
16188     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16189     this.plain = true;
16190     var di = new Roo.menu.DateItem(config);
16191     this.add(di);
16192     /**
16193      * The {@link Roo.DatePicker} instance for this DateMenu
16194      * @type DatePicker
16195      */
16196     this.picker = di.picker;
16197     /**
16198      * @event select
16199      * @param {DatePicker} picker
16200      * @param {Date} date
16201      */
16202     this.relayEvents(di, ["select"]);
16203     this.on('beforeshow', function(){
16204         if(this.picker){
16205             this.picker.hideMonthPicker(false);
16206         }
16207     }, this);
16208 };
16209 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16210     cls:'x-date-menu'
16211 });/*
16212  * Based on:
16213  * Ext JS Library 1.1.1
16214  * Copyright(c) 2006-2007, Ext JS, LLC.
16215  *
16216  * Originally Released Under LGPL - original licence link has changed is not relivant.
16217  *
16218  * Fork - LGPL
16219  * <script type="text/javascript">
16220  */
16221  
16222
16223 /**
16224  * @class Roo.menu.ColorMenu
16225  * @extends Roo.menu.Menu
16226  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16227  * @constructor
16228  * Creates a new ColorMenu
16229  * @param {Object} config Configuration options
16230  */
16231 Roo.menu.ColorMenu = function(config){
16232     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16233     this.plain = true;
16234     var ci = new Roo.menu.ColorItem(config);
16235     this.add(ci);
16236     /**
16237      * The {@link Roo.ColorPalette} instance for this ColorMenu
16238      * @type ColorPalette
16239      */
16240     this.palette = ci.palette;
16241     /**
16242      * @event select
16243      * @param {ColorPalette} palette
16244      * @param {String} color
16245      */
16246     this.relayEvents(ci, ["select"]);
16247 };
16248 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16249  * Based on:
16250  * Ext JS Library 1.1.1
16251  * Copyright(c) 2006-2007, Ext JS, LLC.
16252  *
16253  * Originally Released Under LGPL - original licence link has changed is not relivant.
16254  *
16255  * Fork - LGPL
16256  * <script type="text/javascript">
16257  */
16258  
16259 /**
16260  * @class Roo.form.Field
16261  * @extends Roo.BoxComponent
16262  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16263  * @constructor
16264  * Creates a new Field
16265  * @param {Object} config Configuration options
16266  */
16267 Roo.form.Field = function(config){
16268     Roo.form.Field.superclass.constructor.call(this, config);
16269 };
16270
16271 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16272     /**
16273      * @cfg {String} fieldLabel Label to use when rendering a form.
16274      */
16275        /**
16276      * @cfg {String} qtip Mouse over tip
16277      */
16278      
16279     /**
16280      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16281      */
16282     invalidClass : "x-form-invalid",
16283     /**
16284      * @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")
16285      */
16286     invalidText : "The value in this field is invalid",
16287     /**
16288      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16289      */
16290     focusClass : "x-form-focus",
16291     /**
16292      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16293       automatic validation (defaults to "keyup").
16294      */
16295     validationEvent : "keyup",
16296     /**
16297      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16298      */
16299     validateOnBlur : true,
16300     /**
16301      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16302      */
16303     validationDelay : 250,
16304     /**
16305      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16306      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16307      */
16308     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16309     /**
16310      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16311      */
16312     fieldClass : "x-form-field",
16313     /**
16314      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16315      *<pre>
16316 Value         Description
16317 -----------   ----------------------------------------------------------------------
16318 qtip          Display a quick tip when the user hovers over the field
16319 title         Display a default browser title attribute popup
16320 under         Add a block div beneath the field containing the error text
16321 side          Add an error icon to the right of the field with a popup on hover
16322 [element id]  Add the error text directly to the innerHTML of the specified element
16323 </pre>
16324      */
16325     msgTarget : 'qtip',
16326     /**
16327      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16328      */
16329     msgFx : 'normal',
16330
16331     /**
16332      * @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.
16333      */
16334     readOnly : false,
16335
16336     /**
16337      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16338      */
16339     disabled : false,
16340
16341     /**
16342      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16343      */
16344     inputType : undefined,
16345     
16346     /**
16347      * @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).
16348          */
16349         tabIndex : undefined,
16350         
16351     // private
16352     isFormField : true,
16353
16354     // private
16355     hasFocus : false,
16356     /**
16357      * @property {Roo.Element} fieldEl
16358      * Element Containing the rendered Field (with label etc.)
16359      */
16360     /**
16361      * @cfg {Mixed} value A value to initialize this field with.
16362      */
16363     value : undefined,
16364
16365     /**
16366      * @cfg {String} name The field's HTML name attribute.
16367      */
16368     /**
16369      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16370      */
16371     // private
16372     loadedValue : false,
16373      
16374      
16375         // private ??
16376         initComponent : function(){
16377         Roo.form.Field.superclass.initComponent.call(this);
16378         this.addEvents({
16379             /**
16380              * @event focus
16381              * Fires when this field receives input focus.
16382              * @param {Roo.form.Field} this
16383              */
16384             focus : true,
16385             /**
16386              * @event blur
16387              * Fires when this field loses input focus.
16388              * @param {Roo.form.Field} this
16389              */
16390             blur : true,
16391             /**
16392              * @event specialkey
16393              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16394              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16395              * @param {Roo.form.Field} this
16396              * @param {Roo.EventObject} e The event object
16397              */
16398             specialkey : true,
16399             /**
16400              * @event change
16401              * Fires just before the field blurs if the field value has changed.
16402              * @param {Roo.form.Field} this
16403              * @param {Mixed} newValue The new value
16404              * @param {Mixed} oldValue The original value
16405              */
16406             change : true,
16407             /**
16408              * @event invalid
16409              * Fires after the field has been marked as invalid.
16410              * @param {Roo.form.Field} this
16411              * @param {String} msg The validation message
16412              */
16413             invalid : true,
16414             /**
16415              * @event valid
16416              * Fires after the field has been validated with no errors.
16417              * @param {Roo.form.Field} this
16418              */
16419             valid : true,
16420              /**
16421              * @event keyup
16422              * Fires after the key up
16423              * @param {Roo.form.Field} this
16424              * @param {Roo.EventObject}  e The event Object
16425              */
16426             keyup : true
16427         });
16428     },
16429
16430     /**
16431      * Returns the name attribute of the field if available
16432      * @return {String} name The field name
16433      */
16434     getName: function(){
16435          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16436     },
16437
16438     // private
16439     onRender : function(ct, position){
16440         Roo.form.Field.superclass.onRender.call(this, ct, position);
16441         if(!this.el){
16442             var cfg = this.getAutoCreate();
16443             if(!cfg.name){
16444                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16445             }
16446             if (!cfg.name.length) {
16447                 delete cfg.name;
16448             }
16449             if(this.inputType){
16450                 cfg.type = this.inputType;
16451             }
16452             this.el = ct.createChild(cfg, position);
16453         }
16454         var type = this.el.dom.type;
16455         if(type){
16456             if(type == 'password'){
16457                 type = 'text';
16458             }
16459             this.el.addClass('x-form-'+type);
16460         }
16461         if(this.readOnly){
16462             this.el.dom.readOnly = true;
16463         }
16464         if(this.tabIndex !== undefined){
16465             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16466         }
16467
16468         this.el.addClass([this.fieldClass, this.cls]);
16469         this.initValue();
16470     },
16471
16472     /**
16473      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16474      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16475      * @return {Roo.form.Field} this
16476      */
16477     applyTo : function(target){
16478         this.allowDomMove = false;
16479         this.el = Roo.get(target);
16480         this.render(this.el.dom.parentNode);
16481         return this;
16482     },
16483
16484     // private
16485     initValue : function(){
16486         if(this.value !== undefined){
16487             this.setValue(this.value);
16488         }else if(this.el.dom.value.length > 0){
16489             this.setValue(this.el.dom.value);
16490         }
16491     },
16492
16493     /**
16494      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16495      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16496      */
16497     isDirty : function() {
16498         if(this.disabled) {
16499             return false;
16500         }
16501         return String(this.getValue()) !== String(this.originalValue);
16502     },
16503
16504     /**
16505      * stores the current value in loadedValue
16506      */
16507     resetHasChanged : function()
16508     {
16509         this.loadedValue = String(this.getValue());
16510     },
16511     /**
16512      * checks the current value against the 'loaded' value.
16513      * Note - will return false if 'resetHasChanged' has not been called first.
16514      */
16515     hasChanged : function()
16516     {
16517         if(this.disabled || this.readOnly) {
16518             return false;
16519         }
16520         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16521     },
16522     
16523     
16524     
16525     // private
16526     afterRender : function(){
16527         Roo.form.Field.superclass.afterRender.call(this);
16528         this.initEvents();
16529     },
16530
16531     // private
16532     fireKey : function(e){
16533         //Roo.log('field ' + e.getKey());
16534         if(e.isNavKeyPress()){
16535             this.fireEvent("specialkey", this, e);
16536         }
16537     },
16538
16539     /**
16540      * Resets the current field value to the originally loaded value and clears any validation messages
16541      */
16542     reset : function(){
16543         this.setValue(this.resetValue);
16544         this.originalValue = this.getValue();
16545         this.clearInvalid();
16546     },
16547
16548     // private
16549     initEvents : function(){
16550         // safari killled keypress - so keydown is now used..
16551         this.el.on("keydown" , this.fireKey,  this);
16552         this.el.on("focus", this.onFocus,  this);
16553         this.el.on("blur", this.onBlur,  this);
16554         this.el.relayEvent('keyup', this);
16555
16556         // reference to original value for reset
16557         this.originalValue = this.getValue();
16558         this.resetValue =  this.getValue();
16559     },
16560
16561     // private
16562     onFocus : function(){
16563         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16564             this.el.addClass(this.focusClass);
16565         }
16566         if(!this.hasFocus){
16567             this.hasFocus = true;
16568             this.startValue = this.getValue();
16569             this.fireEvent("focus", this);
16570         }
16571     },
16572
16573     beforeBlur : Roo.emptyFn,
16574
16575     // private
16576     onBlur : function(){
16577         this.beforeBlur();
16578         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16579             this.el.removeClass(this.focusClass);
16580         }
16581         this.hasFocus = false;
16582         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16583             this.validate();
16584         }
16585         var v = this.getValue();
16586         if(String(v) !== String(this.startValue)){
16587             this.fireEvent('change', this, v, this.startValue);
16588         }
16589         this.fireEvent("blur", this);
16590     },
16591
16592     /**
16593      * Returns whether or not the field value is currently valid
16594      * @param {Boolean} preventMark True to disable marking the field invalid
16595      * @return {Boolean} True if the value is valid, else false
16596      */
16597     isValid : function(preventMark){
16598         if(this.disabled){
16599             return true;
16600         }
16601         var restore = this.preventMark;
16602         this.preventMark = preventMark === true;
16603         var v = this.validateValue(this.processValue(this.getRawValue()));
16604         this.preventMark = restore;
16605         return v;
16606     },
16607
16608     /**
16609      * Validates the field value
16610      * @return {Boolean} True if the value is valid, else false
16611      */
16612     validate : function(){
16613         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16614             this.clearInvalid();
16615             return true;
16616         }
16617         return false;
16618     },
16619
16620     processValue : function(value){
16621         return value;
16622     },
16623
16624     // private
16625     // Subclasses should provide the validation implementation by overriding this
16626     validateValue : function(value){
16627         return true;
16628     },
16629
16630     /**
16631      * Mark this field as invalid
16632      * @param {String} msg The validation message
16633      */
16634     markInvalid : function(msg){
16635         if(!this.rendered || this.preventMark){ // not rendered
16636             return;
16637         }
16638         
16639         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16640         
16641         obj.el.addClass(this.invalidClass);
16642         msg = msg || this.invalidText;
16643         switch(this.msgTarget){
16644             case 'qtip':
16645                 obj.el.dom.qtip = msg;
16646                 obj.el.dom.qclass = 'x-form-invalid-tip';
16647                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16648                     Roo.QuickTips.enable();
16649                 }
16650                 break;
16651             case 'title':
16652                 this.el.dom.title = msg;
16653                 break;
16654             case 'under':
16655                 if(!this.errorEl){
16656                     var elp = this.el.findParent('.x-form-element', 5, true);
16657                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16658                     this.errorEl.setWidth(elp.getWidth(true)-20);
16659                 }
16660                 this.errorEl.update(msg);
16661                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16662                 break;
16663             case 'side':
16664                 if(!this.errorIcon){
16665                     var elp = this.el.findParent('.x-form-element', 5, true);
16666                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16667                 }
16668                 this.alignErrorIcon();
16669                 this.errorIcon.dom.qtip = msg;
16670                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16671                 this.errorIcon.show();
16672                 this.on('resize', this.alignErrorIcon, this);
16673                 break;
16674             default:
16675                 var t = Roo.getDom(this.msgTarget);
16676                 t.innerHTML = msg;
16677                 t.style.display = this.msgDisplay;
16678                 break;
16679         }
16680         this.fireEvent('invalid', this, msg);
16681     },
16682
16683     // private
16684     alignErrorIcon : function(){
16685         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16686     },
16687
16688     /**
16689      * Clear any invalid styles/messages for this field
16690      */
16691     clearInvalid : function(){
16692         if(!this.rendered || this.preventMark){ // not rendered
16693             return;
16694         }
16695         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16696         
16697         obj.el.removeClass(this.invalidClass);
16698         switch(this.msgTarget){
16699             case 'qtip':
16700                 obj.el.dom.qtip = '';
16701                 break;
16702             case 'title':
16703                 this.el.dom.title = '';
16704                 break;
16705             case 'under':
16706                 if(this.errorEl){
16707                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16708                 }
16709                 break;
16710             case 'side':
16711                 if(this.errorIcon){
16712                     this.errorIcon.dom.qtip = '';
16713                     this.errorIcon.hide();
16714                     this.un('resize', this.alignErrorIcon, this);
16715                 }
16716                 break;
16717             default:
16718                 var t = Roo.getDom(this.msgTarget);
16719                 t.innerHTML = '';
16720                 t.style.display = 'none';
16721                 break;
16722         }
16723         this.fireEvent('valid', this);
16724     },
16725
16726     /**
16727      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16728      * @return {Mixed} value The field value
16729      */
16730     getRawValue : function(){
16731         var v = this.el.getValue();
16732         
16733         return v;
16734     },
16735
16736     /**
16737      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16738      * @return {Mixed} value The field value
16739      */
16740     getValue : function(){
16741         var v = this.el.getValue();
16742          
16743         return v;
16744     },
16745
16746     /**
16747      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16748      * @param {Mixed} value The value to set
16749      */
16750     setRawValue : function(v){
16751         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16752     },
16753
16754     /**
16755      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16756      * @param {Mixed} value The value to set
16757      */
16758     setValue : function(v){
16759         this.value = v;
16760         if(this.rendered){
16761             this.el.dom.value = (v === null || v === undefined ? '' : v);
16762              this.validate();
16763         }
16764     },
16765
16766     adjustSize : function(w, h){
16767         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16768         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16769         return s;
16770     },
16771
16772     adjustWidth : function(tag, w){
16773         tag = tag.toLowerCase();
16774         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16775             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16776                 if(tag == 'input'){
16777                     return w + 2;
16778                 }
16779                 if(tag == 'textarea'){
16780                     return w-2;
16781                 }
16782             }else if(Roo.isOpera){
16783                 if(tag == 'input'){
16784                     return w + 2;
16785                 }
16786                 if(tag == 'textarea'){
16787                     return w-2;
16788                 }
16789             }
16790         }
16791         return w;
16792     }
16793 });
16794
16795
16796 // anything other than normal should be considered experimental
16797 Roo.form.Field.msgFx = {
16798     normal : {
16799         show: function(msgEl, f){
16800             msgEl.setDisplayed('block');
16801         },
16802
16803         hide : function(msgEl, f){
16804             msgEl.setDisplayed(false).update('');
16805         }
16806     },
16807
16808     slide : {
16809         show: function(msgEl, f){
16810             msgEl.slideIn('t', {stopFx:true});
16811         },
16812
16813         hide : function(msgEl, f){
16814             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16815         }
16816     },
16817
16818     slideRight : {
16819         show: function(msgEl, f){
16820             msgEl.fixDisplay();
16821             msgEl.alignTo(f.el, 'tl-tr');
16822             msgEl.slideIn('l', {stopFx:true});
16823         },
16824
16825         hide : function(msgEl, f){
16826             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16827         }
16828     }
16829 };/*
16830  * Based on:
16831  * Ext JS Library 1.1.1
16832  * Copyright(c) 2006-2007, Ext JS, LLC.
16833  *
16834  * Originally Released Under LGPL - original licence link has changed is not relivant.
16835  *
16836  * Fork - LGPL
16837  * <script type="text/javascript">
16838  */
16839  
16840
16841 /**
16842  * @class Roo.form.TextField
16843  * @extends Roo.form.Field
16844  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16845  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16846  * @constructor
16847  * Creates a new TextField
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.form.TextField = function(config){
16851     Roo.form.TextField.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event autosize
16855          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16856          * according to the default logic, but this event provides a hook for the developer to apply additional
16857          * logic at runtime to resize the field if needed.
16858              * @param {Roo.form.Field} this This text field
16859              * @param {Number} width The new field width
16860              */
16861         autosize : true
16862     });
16863 };
16864
16865 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16866     /**
16867      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16868      */
16869     grow : false,
16870     /**
16871      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16872      */
16873     growMin : 30,
16874     /**
16875      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16876      */
16877     growMax : 800,
16878     /**
16879      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16880      */
16881     vtype : null,
16882     /**
16883      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16884      */
16885     maskRe : null,
16886     /**
16887      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16888      */
16889     disableKeyFilter : false,
16890     /**
16891      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16892      */
16893     allowBlank : true,
16894     /**
16895      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16896      */
16897     minLength : 0,
16898     /**
16899      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16900      */
16901     maxLength : Number.MAX_VALUE,
16902     /**
16903      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16904      */
16905     minLengthText : "The minimum length for this field is {0}",
16906     /**
16907      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16908      */
16909     maxLengthText : "The maximum length for this field is {0}",
16910     /**
16911      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16912      */
16913     selectOnFocus : false,
16914     /**
16915      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16916      */
16917     blankText : "This field is required",
16918     /**
16919      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16920      * If available, this function will be called only after the basic validators all return true, and will be passed the
16921      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16922      */
16923     validator : null,
16924     /**
16925      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16926      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16927      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16928      */
16929     regex : null,
16930     /**
16931      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16932      */
16933     regexText : "",
16934     /**
16935      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16936      */
16937     emptyText : null,
16938    
16939
16940     // private
16941     initEvents : function()
16942     {
16943         if (this.emptyText) {
16944             this.el.attr('placeholder', this.emptyText);
16945         }
16946         
16947         Roo.form.TextField.superclass.initEvents.call(this);
16948         if(this.validationEvent == 'keyup'){
16949             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16950             this.el.on('keyup', this.filterValidation, this);
16951         }
16952         else if(this.validationEvent !== false){
16953             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16954         }
16955         
16956         if(this.selectOnFocus){
16957             this.on("focus", this.preFocus, this);
16958             
16959         }
16960         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16961             this.el.on("keypress", this.filterKeys, this);
16962         }
16963         if(this.grow){
16964             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16965             this.el.on("click", this.autoSize,  this);
16966         }
16967         if(this.el.is('input[type=password]') && Roo.isSafari){
16968             this.el.on('keydown', this.SafariOnKeyDown, this);
16969         }
16970     },
16971
16972     processValue : function(value){
16973         if(this.stripCharsRe){
16974             var newValue = value.replace(this.stripCharsRe, '');
16975             if(newValue !== value){
16976                 this.setRawValue(newValue);
16977                 return newValue;
16978             }
16979         }
16980         return value;
16981     },
16982
16983     filterValidation : function(e){
16984         if(!e.isNavKeyPress()){
16985             this.validationTask.delay(this.validationDelay);
16986         }
16987     },
16988
16989     // private
16990     onKeyUp : function(e){
16991         if(!e.isNavKeyPress()){
16992             this.autoSize();
16993         }
16994     },
16995
16996     /**
16997      * Resets the current field value to the originally-loaded value and clears any validation messages.
16998      *  
16999      */
17000     reset : function(){
17001         Roo.form.TextField.superclass.reset.call(this);
17002        
17003     },
17004
17005     
17006     // private
17007     preFocus : function(){
17008         
17009         if(this.selectOnFocus){
17010             this.el.dom.select();
17011         }
17012     },
17013
17014     
17015     // private
17016     filterKeys : function(e){
17017         var k = e.getKey();
17018         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17019             return;
17020         }
17021         var c = e.getCharCode(), cc = String.fromCharCode(c);
17022         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17023             return;
17024         }
17025         if(!this.maskRe.test(cc)){
17026             e.stopEvent();
17027         }
17028     },
17029
17030     setValue : function(v){
17031         
17032         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17033         
17034         this.autoSize();
17035     },
17036
17037     /**
17038      * Validates a value according to the field's validation rules and marks the field as invalid
17039      * if the validation fails
17040      * @param {Mixed} value The value to validate
17041      * @return {Boolean} True if the value is valid, else false
17042      */
17043     validateValue : function(value){
17044         if(value.length < 1)  { // if it's blank
17045              if(this.allowBlank){
17046                 this.clearInvalid();
17047                 return true;
17048              }else{
17049                 this.markInvalid(this.blankText);
17050                 return false;
17051              }
17052         }
17053         if(value.length < this.minLength){
17054             this.markInvalid(String.format(this.minLengthText, this.minLength));
17055             return false;
17056         }
17057         if(value.length > this.maxLength){
17058             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17059             return false;
17060         }
17061         if(this.vtype){
17062             var vt = Roo.form.VTypes;
17063             if(!vt[this.vtype](value, this)){
17064                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17065                 return false;
17066             }
17067         }
17068         if(typeof this.validator == "function"){
17069             var msg = this.validator(value);
17070             if(msg !== true){
17071                 this.markInvalid(msg);
17072                 return false;
17073             }
17074         }
17075         if(this.regex && !this.regex.test(value)){
17076             this.markInvalid(this.regexText);
17077             return false;
17078         }
17079         return true;
17080     },
17081
17082     /**
17083      * Selects text in this field
17084      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17085      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17086      */
17087     selectText : function(start, end){
17088         var v = this.getRawValue();
17089         if(v.length > 0){
17090             start = start === undefined ? 0 : start;
17091             end = end === undefined ? v.length : end;
17092             var d = this.el.dom;
17093             if(d.setSelectionRange){
17094                 d.setSelectionRange(start, end);
17095             }else if(d.createTextRange){
17096                 var range = d.createTextRange();
17097                 range.moveStart("character", start);
17098                 range.moveEnd("character", v.length-end);
17099                 range.select();
17100             }
17101         }
17102     },
17103
17104     /**
17105      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17106      * This only takes effect if grow = true, and fires the autosize event.
17107      */
17108     autoSize : function(){
17109         if(!this.grow || !this.rendered){
17110             return;
17111         }
17112         if(!this.metrics){
17113             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17114         }
17115         var el = this.el;
17116         var v = el.dom.value;
17117         var d = document.createElement('div');
17118         d.appendChild(document.createTextNode(v));
17119         v = d.innerHTML;
17120         d = null;
17121         v += "&#160;";
17122         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17123         this.el.setWidth(w);
17124         this.fireEvent("autosize", this, w);
17125     },
17126     
17127     // private
17128     SafariOnKeyDown : function(event)
17129     {
17130         // this is a workaround for a password hang bug on chrome/ webkit.
17131         
17132         var isSelectAll = false;
17133         
17134         if(this.el.dom.selectionEnd > 0){
17135             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17136         }
17137         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17138             event.preventDefault();
17139             this.setValue('');
17140             return;
17141         }
17142         
17143         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17144             
17145             event.preventDefault();
17146             // this is very hacky as keydown always get's upper case.
17147             
17148             var cc = String.fromCharCode(event.getCharCode());
17149             
17150             
17151             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17152             
17153         }
17154         
17155         
17156     }
17157 });/*
17158  * Based on:
17159  * Ext JS Library 1.1.1
17160  * Copyright(c) 2006-2007, Ext JS, LLC.
17161  *
17162  * Originally Released Under LGPL - original licence link has changed is not relivant.
17163  *
17164  * Fork - LGPL
17165  * <script type="text/javascript">
17166  */
17167  
17168 /**
17169  * @class Roo.form.Hidden
17170  * @extends Roo.form.TextField
17171  * Simple Hidden element used on forms 
17172  * 
17173  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17174  * 
17175  * @constructor
17176  * Creates a new Hidden form element.
17177  * @param {Object} config Configuration options
17178  */
17179
17180
17181
17182 // easy hidden field...
17183 Roo.form.Hidden = function(config){
17184     Roo.form.Hidden.superclass.constructor.call(this, config);
17185 };
17186   
17187 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17188     fieldLabel:      '',
17189     inputType:      'hidden',
17190     width:          50,
17191     allowBlank:     true,
17192     labelSeparator: '',
17193     hidden:         true,
17194     itemCls :       'x-form-item-display-none'
17195
17196
17197 });
17198
17199
17200 /*
17201  * Based on:
17202  * Ext JS Library 1.1.1
17203  * Copyright(c) 2006-2007, Ext JS, LLC.
17204  *
17205  * Originally Released Under LGPL - original licence link has changed is not relivant.
17206  *
17207  * Fork - LGPL
17208  * <script type="text/javascript">
17209  */
17210  
17211 /**
17212  * @class Roo.form.TriggerField
17213  * @extends Roo.form.TextField
17214  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17215  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17216  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17217  * for which you can provide a custom implementation.  For example:
17218  * <pre><code>
17219 var trigger = new Roo.form.TriggerField();
17220 trigger.onTriggerClick = myTriggerFn;
17221 trigger.applyTo('my-field');
17222 </code></pre>
17223  *
17224  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17225  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17226  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17227  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17228  * @constructor
17229  * Create a new TriggerField.
17230  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17231  * to the base TextField)
17232  */
17233 Roo.form.TriggerField = function(config){
17234     this.mimicing = false;
17235     Roo.form.TriggerField.superclass.constructor.call(this, config);
17236 };
17237
17238 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17239     /**
17240      * @cfg {String} triggerClass A CSS class to apply to the trigger
17241      */
17242     /**
17243      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17244      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17245      */
17246     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17247     /**
17248      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17249      */
17250     hideTrigger:false,
17251
17252     /** @cfg {Boolean} grow @hide */
17253     /** @cfg {Number} growMin @hide */
17254     /** @cfg {Number} growMax @hide */
17255
17256     /**
17257      * @hide 
17258      * @method
17259      */
17260     autoSize: Roo.emptyFn,
17261     // private
17262     monitorTab : true,
17263     // private
17264     deferHeight : true,
17265
17266     
17267     actionMode : 'wrap',
17268     // private
17269     onResize : function(w, h){
17270         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17271         if(typeof w == 'number'){
17272             var x = w - this.trigger.getWidth();
17273             this.el.setWidth(this.adjustWidth('input', x));
17274             this.trigger.setStyle('left', x+'px');
17275         }
17276     },
17277
17278     // private
17279     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17280
17281     // private
17282     getResizeEl : function(){
17283         return this.wrap;
17284     },
17285
17286     // private
17287     getPositionEl : function(){
17288         return this.wrap;
17289     },
17290
17291     // private
17292     alignErrorIcon : function(){
17293         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17294     },
17295
17296     // private
17297     onRender : function(ct, position){
17298         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17299         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17300         this.trigger = this.wrap.createChild(this.triggerConfig ||
17301                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17302         if(this.hideTrigger){
17303             this.trigger.setDisplayed(false);
17304         }
17305         this.initTrigger();
17306         if(!this.width){
17307             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17308         }
17309     },
17310
17311     // private
17312     initTrigger : function(){
17313         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17314         this.trigger.addClassOnOver('x-form-trigger-over');
17315         this.trigger.addClassOnClick('x-form-trigger-click');
17316     },
17317
17318     // private
17319     onDestroy : function(){
17320         if(this.trigger){
17321             this.trigger.removeAllListeners();
17322             this.trigger.remove();
17323         }
17324         if(this.wrap){
17325             this.wrap.remove();
17326         }
17327         Roo.form.TriggerField.superclass.onDestroy.call(this);
17328     },
17329
17330     // private
17331     onFocus : function(){
17332         Roo.form.TriggerField.superclass.onFocus.call(this);
17333         if(!this.mimicing){
17334             this.wrap.addClass('x-trigger-wrap-focus');
17335             this.mimicing = true;
17336             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17337             if(this.monitorTab){
17338                 this.el.on("keydown", this.checkTab, this);
17339             }
17340         }
17341     },
17342
17343     // private
17344     checkTab : function(e){
17345         if(e.getKey() == e.TAB){
17346             this.triggerBlur();
17347         }
17348     },
17349
17350     // private
17351     onBlur : function(){
17352         // do nothing
17353     },
17354
17355     // private
17356     mimicBlur : function(e, t){
17357         if(!this.wrap.contains(t) && this.validateBlur()){
17358             this.triggerBlur();
17359         }
17360     },
17361
17362     // private
17363     triggerBlur : function(){
17364         this.mimicing = false;
17365         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17366         if(this.monitorTab){
17367             this.el.un("keydown", this.checkTab, this);
17368         }
17369         this.wrap.removeClass('x-trigger-wrap-focus');
17370         Roo.form.TriggerField.superclass.onBlur.call(this);
17371     },
17372
17373     // private
17374     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17375     validateBlur : function(e, t){
17376         return true;
17377     },
17378
17379     // private
17380     onDisable : function(){
17381         Roo.form.TriggerField.superclass.onDisable.call(this);
17382         if(this.wrap){
17383             this.wrap.addClass('x-item-disabled');
17384         }
17385     },
17386
17387     // private
17388     onEnable : function(){
17389         Roo.form.TriggerField.superclass.onEnable.call(this);
17390         if(this.wrap){
17391             this.wrap.removeClass('x-item-disabled');
17392         }
17393     },
17394
17395     // private
17396     onShow : function(){
17397         var ae = this.getActionEl();
17398         
17399         if(ae){
17400             ae.dom.style.display = '';
17401             ae.dom.style.visibility = 'visible';
17402         }
17403     },
17404
17405     // private
17406     
17407     onHide : function(){
17408         var ae = this.getActionEl();
17409         ae.dom.style.display = 'none';
17410     },
17411
17412     /**
17413      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17414      * by an implementing function.
17415      * @method
17416      * @param {EventObject} e
17417      */
17418     onTriggerClick : Roo.emptyFn
17419 });
17420
17421 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17422 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17423 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17424 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17425     initComponent : function(){
17426         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17427
17428         this.triggerConfig = {
17429             tag:'span', cls:'x-form-twin-triggers', cn:[
17430             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17431             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17432         ]};
17433     },
17434
17435     getTrigger : function(index){
17436         return this.triggers[index];
17437     },
17438
17439     initTrigger : function(){
17440         var ts = this.trigger.select('.x-form-trigger', true);
17441         this.wrap.setStyle('overflow', 'hidden');
17442         var triggerField = this;
17443         ts.each(function(t, all, index){
17444             t.hide = function(){
17445                 var w = triggerField.wrap.getWidth();
17446                 this.dom.style.display = 'none';
17447                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17448             };
17449             t.show = function(){
17450                 var w = triggerField.wrap.getWidth();
17451                 this.dom.style.display = '';
17452                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17453             };
17454             var triggerIndex = 'Trigger'+(index+1);
17455
17456             if(this['hide'+triggerIndex]){
17457                 t.dom.style.display = 'none';
17458             }
17459             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17460             t.addClassOnOver('x-form-trigger-over');
17461             t.addClassOnClick('x-form-trigger-click');
17462         }, this);
17463         this.triggers = ts.elements;
17464     },
17465
17466     onTrigger1Click : Roo.emptyFn,
17467     onTrigger2Click : Roo.emptyFn
17468 });/*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478  
17479 /**
17480  * @class Roo.form.TextArea
17481  * @extends Roo.form.TextField
17482  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17483  * support for auto-sizing.
17484  * @constructor
17485  * Creates a new TextArea
17486  * @param {Object} config Configuration options
17487  */
17488 Roo.form.TextArea = function(config){
17489     Roo.form.TextArea.superclass.constructor.call(this, config);
17490     // these are provided exchanges for backwards compat
17491     // minHeight/maxHeight were replaced by growMin/growMax to be
17492     // compatible with TextField growing config values
17493     if(this.minHeight !== undefined){
17494         this.growMin = this.minHeight;
17495     }
17496     if(this.maxHeight !== undefined){
17497         this.growMax = this.maxHeight;
17498     }
17499 };
17500
17501 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17502     /**
17503      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17504      */
17505     growMin : 60,
17506     /**
17507      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17508      */
17509     growMax: 1000,
17510     /**
17511      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17512      * in the field (equivalent to setting overflow: hidden, defaults to false)
17513      */
17514     preventScrollbars: false,
17515     /**
17516      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17517      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17518      */
17519
17520     // private
17521     onRender : function(ct, position){
17522         if(!this.el){
17523             this.defaultAutoCreate = {
17524                 tag: "textarea",
17525                 style:"width:300px;height:60px;",
17526                 autocomplete: "new-password"
17527             };
17528         }
17529         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17530         if(this.grow){
17531             this.textSizeEl = Roo.DomHelper.append(document.body, {
17532                 tag: "pre", cls: "x-form-grow-sizer"
17533             });
17534             if(this.preventScrollbars){
17535                 this.el.setStyle("overflow", "hidden");
17536             }
17537             this.el.setHeight(this.growMin);
17538         }
17539     },
17540
17541     onDestroy : function(){
17542         if(this.textSizeEl){
17543             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17544         }
17545         Roo.form.TextArea.superclass.onDestroy.call(this);
17546     },
17547
17548     // private
17549     onKeyUp : function(e){
17550         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17551             this.autoSize();
17552         }
17553     },
17554
17555     /**
17556      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17557      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17558      */
17559     autoSize : function(){
17560         if(!this.grow || !this.textSizeEl){
17561             return;
17562         }
17563         var el = this.el;
17564         var v = el.dom.value;
17565         var ts = this.textSizeEl;
17566
17567         ts.innerHTML = '';
17568         ts.appendChild(document.createTextNode(v));
17569         v = ts.innerHTML;
17570
17571         Roo.fly(ts).setWidth(this.el.getWidth());
17572         if(v.length < 1){
17573             v = "&#160;&#160;";
17574         }else{
17575             if(Roo.isIE){
17576                 v = v.replace(/\n/g, '<p>&#160;</p>');
17577             }
17578             v += "&#160;\n&#160;";
17579         }
17580         ts.innerHTML = v;
17581         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17582         if(h != this.lastHeight){
17583             this.lastHeight = h;
17584             this.el.setHeight(h);
17585             this.fireEvent("autosize", this, h);
17586         }
17587     }
17588 });/*
17589  * Based on:
17590  * Ext JS Library 1.1.1
17591  * Copyright(c) 2006-2007, Ext JS, LLC.
17592  *
17593  * Originally Released Under LGPL - original licence link has changed is not relivant.
17594  *
17595  * Fork - LGPL
17596  * <script type="text/javascript">
17597  */
17598  
17599
17600 /**
17601  * @class Roo.form.NumberField
17602  * @extends Roo.form.TextField
17603  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17604  * @constructor
17605  * Creates a new NumberField
17606  * @param {Object} config Configuration options
17607  */
17608 Roo.form.NumberField = function(config){
17609     Roo.form.NumberField.superclass.constructor.call(this, config);
17610 };
17611
17612 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17613     /**
17614      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17615      */
17616     fieldClass: "x-form-field x-form-num-field",
17617     /**
17618      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17619      */
17620     allowDecimals : true,
17621     /**
17622      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17623      */
17624     decimalSeparator : ".",
17625     /**
17626      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17627      */
17628     decimalPrecision : 2,
17629     /**
17630      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17631      */
17632     allowNegative : true,
17633     /**
17634      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17635      */
17636     minValue : Number.NEGATIVE_INFINITY,
17637     /**
17638      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17639      */
17640     maxValue : Number.MAX_VALUE,
17641     /**
17642      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17643      */
17644     minText : "The minimum value for this field is {0}",
17645     /**
17646      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17647      */
17648     maxText : "The maximum value for this field is {0}",
17649     /**
17650      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17651      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17652      */
17653     nanText : "{0} is not a valid number",
17654
17655     // private
17656     initEvents : function(){
17657         Roo.form.NumberField.superclass.initEvents.call(this);
17658         var allowed = "0123456789";
17659         if(this.allowDecimals){
17660             allowed += this.decimalSeparator;
17661         }
17662         if(this.allowNegative){
17663             allowed += "-";
17664         }
17665         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17666         var keyPress = function(e){
17667             var k = e.getKey();
17668             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17669                 return;
17670             }
17671             var c = e.getCharCode();
17672             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17673                 e.stopEvent();
17674             }
17675         };
17676         this.el.on("keypress", keyPress, this);
17677     },
17678
17679     // private
17680     validateValue : function(value){
17681         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17682             return false;
17683         }
17684         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17685              return true;
17686         }
17687         var num = this.parseValue(value);
17688         if(isNaN(num)){
17689             this.markInvalid(String.format(this.nanText, value));
17690             return false;
17691         }
17692         if(num < this.minValue){
17693             this.markInvalid(String.format(this.minText, this.minValue));
17694             return false;
17695         }
17696         if(num > this.maxValue){
17697             this.markInvalid(String.format(this.maxText, this.maxValue));
17698             return false;
17699         }
17700         return true;
17701     },
17702
17703     getValue : function(){
17704         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17705     },
17706
17707     // private
17708     parseValue : function(value){
17709         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17710         return isNaN(value) ? '' : value;
17711     },
17712
17713     // private
17714     fixPrecision : function(value){
17715         var nan = isNaN(value);
17716         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17717             return nan ? '' : value;
17718         }
17719         return parseFloat(value).toFixed(this.decimalPrecision);
17720     },
17721
17722     setValue : function(v){
17723         v = this.fixPrecision(v);
17724         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17725     },
17726
17727     // private
17728     decimalPrecisionFcn : function(v){
17729         return Math.floor(v);
17730     },
17731
17732     beforeBlur : function(){
17733         var v = this.parseValue(this.getRawValue());
17734         if(v){
17735             this.setValue(v);
17736         }
17737     }
17738 });/*
17739  * Based on:
17740  * Ext JS Library 1.1.1
17741  * Copyright(c) 2006-2007, Ext JS, LLC.
17742  *
17743  * Originally Released Under LGPL - original licence link has changed is not relivant.
17744  *
17745  * Fork - LGPL
17746  * <script type="text/javascript">
17747  */
17748  
17749 /**
17750  * @class Roo.form.DateField
17751  * @extends Roo.form.TriggerField
17752  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17753 * @constructor
17754 * Create a new DateField
17755 * @param {Object} config
17756  */
17757 Roo.form.DateField = function(config){
17758     Roo.form.DateField.superclass.constructor.call(this, config);
17759     
17760       this.addEvents({
17761          
17762         /**
17763          * @event select
17764          * Fires when a date is selected
17765              * @param {Roo.form.DateField} combo This combo box
17766              * @param {Date} date The date selected
17767              */
17768         'select' : true
17769          
17770     });
17771     
17772     
17773     if(typeof this.minValue == "string") {
17774         this.minValue = this.parseDate(this.minValue);
17775     }
17776     if(typeof this.maxValue == "string") {
17777         this.maxValue = this.parseDate(this.maxValue);
17778     }
17779     this.ddMatch = null;
17780     if(this.disabledDates){
17781         var dd = this.disabledDates;
17782         var re = "(?:";
17783         for(var i = 0; i < dd.length; i++){
17784             re += dd[i];
17785             if(i != dd.length-1) {
17786                 re += "|";
17787             }
17788         }
17789         this.ddMatch = new RegExp(re + ")");
17790     }
17791 };
17792
17793 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17794     /**
17795      * @cfg {String} format
17796      * The default date format string which can be overriden for localization support.  The format must be
17797      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17798      */
17799     format : "m/d/y",
17800     /**
17801      * @cfg {String} altFormats
17802      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17803      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17804      */
17805     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17806     /**
17807      * @cfg {Array} disabledDays
17808      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17809      */
17810     disabledDays : null,
17811     /**
17812      * @cfg {String} disabledDaysText
17813      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17814      */
17815     disabledDaysText : "Disabled",
17816     /**
17817      * @cfg {Array} disabledDates
17818      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17819      * expression so they are very powerful. Some examples:
17820      * <ul>
17821      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17822      * <li>["03/08", "09/16"] would disable those days for every year</li>
17823      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17824      * <li>["03/../2006"] would disable every day in March 2006</li>
17825      * <li>["^03"] would disable every day in every March</li>
17826      * </ul>
17827      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17828      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17829      */
17830     disabledDates : null,
17831     /**
17832      * @cfg {String} disabledDatesText
17833      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17834      */
17835     disabledDatesText : "Disabled",
17836     /**
17837      * @cfg {Date/String} minValue
17838      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17839      * valid format (defaults to null).
17840      */
17841     minValue : null,
17842     /**
17843      * @cfg {Date/String} maxValue
17844      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17845      * valid format (defaults to null).
17846      */
17847     maxValue : null,
17848     /**
17849      * @cfg {String} minText
17850      * The error text to display when the date in the cell is before minValue (defaults to
17851      * 'The date in this field must be after {minValue}').
17852      */
17853     minText : "The date in this field must be equal to or after {0}",
17854     /**
17855      * @cfg {String} maxText
17856      * The error text to display when the date in the cell is after maxValue (defaults to
17857      * 'The date in this field must be before {maxValue}').
17858      */
17859     maxText : "The date in this field must be equal to or before {0}",
17860     /**
17861      * @cfg {String} invalidText
17862      * The error text to display when the date in the field is invalid (defaults to
17863      * '{value} is not a valid date - it must be in the format {format}').
17864      */
17865     invalidText : "{0} is not a valid date - it must be in the format {1}",
17866     /**
17867      * @cfg {String} triggerClass
17868      * An additional CSS class used to style the trigger button.  The trigger will always get the
17869      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17870      * which displays a calendar icon).
17871      */
17872     triggerClass : 'x-form-date-trigger',
17873     
17874
17875     /**
17876      * @cfg {Boolean} useIso
17877      * if enabled, then the date field will use a hidden field to store the 
17878      * real value as iso formated date. default (false)
17879      */ 
17880     useIso : false,
17881     /**
17882      * @cfg {String/Object} autoCreate
17883      * A DomHelper element spec, or true for a default element spec (defaults to
17884      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17885      */ 
17886     // private
17887     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17888     
17889     // private
17890     hiddenField: false,
17891     
17892     onRender : function(ct, position)
17893     {
17894         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17895         if (this.useIso) {
17896             //this.el.dom.removeAttribute('name'); 
17897             Roo.log("Changing name?");
17898             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17899             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17900                     'before', true);
17901             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17902             // prevent input submission
17903             this.hiddenName = this.name;
17904         }
17905             
17906             
17907     },
17908     
17909     // private
17910     validateValue : function(value)
17911     {
17912         value = this.formatDate(value);
17913         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17914             Roo.log('super failed');
17915             return false;
17916         }
17917         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17918              return true;
17919         }
17920         var svalue = value;
17921         value = this.parseDate(value);
17922         if(!value){
17923             Roo.log('parse date failed' + svalue);
17924             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17925             return false;
17926         }
17927         var time = value.getTime();
17928         if(this.minValue && time < this.minValue.getTime()){
17929             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17930             return false;
17931         }
17932         if(this.maxValue && time > this.maxValue.getTime()){
17933             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17934             return false;
17935         }
17936         if(this.disabledDays){
17937             var day = value.getDay();
17938             for(var i = 0; i < this.disabledDays.length; i++) {
17939                 if(day === this.disabledDays[i]){
17940                     this.markInvalid(this.disabledDaysText);
17941                     return false;
17942                 }
17943             }
17944         }
17945         var fvalue = this.formatDate(value);
17946         if(this.ddMatch && this.ddMatch.test(fvalue)){
17947             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17948             return false;
17949         }
17950         return true;
17951     },
17952
17953     // private
17954     // Provides logic to override the default TriggerField.validateBlur which just returns true
17955     validateBlur : function(){
17956         return !this.menu || !this.menu.isVisible();
17957     },
17958     
17959     getName: function()
17960     {
17961         // returns hidden if it's set..
17962         if (!this.rendered) {return ''};
17963         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17964         
17965     },
17966
17967     /**
17968      * Returns the current date value of the date field.
17969      * @return {Date} The date value
17970      */
17971     getValue : function(){
17972         
17973         return  this.hiddenField ?
17974                 this.hiddenField.value :
17975                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17976     },
17977
17978     /**
17979      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17980      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17981      * (the default format used is "m/d/y").
17982      * <br />Usage:
17983      * <pre><code>
17984 //All of these calls set the same date value (May 4, 2006)
17985
17986 //Pass a date object:
17987 var dt = new Date('5/4/06');
17988 dateField.setValue(dt);
17989
17990 //Pass a date string (default format):
17991 dateField.setValue('5/4/06');
17992
17993 //Pass a date string (custom format):
17994 dateField.format = 'Y-m-d';
17995 dateField.setValue('2006-5-4');
17996 </code></pre>
17997      * @param {String/Date} date The date or valid date string
17998      */
17999     setValue : function(date){
18000         if (this.hiddenField) {
18001             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18002         }
18003         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18004         // make sure the value field is always stored as a date..
18005         this.value = this.parseDate(date);
18006         
18007         
18008     },
18009
18010     // private
18011     parseDate : function(value){
18012         if(!value || value instanceof Date){
18013             return value;
18014         }
18015         var v = Date.parseDate(value, this.format);
18016          if (!v && this.useIso) {
18017             v = Date.parseDate(value, 'Y-m-d');
18018         }
18019         if(!v && this.altFormats){
18020             if(!this.altFormatsArray){
18021                 this.altFormatsArray = this.altFormats.split("|");
18022             }
18023             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18024                 v = Date.parseDate(value, this.altFormatsArray[i]);
18025             }
18026         }
18027         return v;
18028     },
18029
18030     // private
18031     formatDate : function(date, fmt){
18032         return (!date || !(date instanceof Date)) ?
18033                date : date.dateFormat(fmt || this.format);
18034     },
18035
18036     // private
18037     menuListeners : {
18038         select: function(m, d){
18039             
18040             this.setValue(d);
18041             this.fireEvent('select', this, d);
18042         },
18043         show : function(){ // retain focus styling
18044             this.onFocus();
18045         },
18046         hide : function(){
18047             this.focus.defer(10, this);
18048             var ml = this.menuListeners;
18049             this.menu.un("select", ml.select,  this);
18050             this.menu.un("show", ml.show,  this);
18051             this.menu.un("hide", ml.hide,  this);
18052         }
18053     },
18054
18055     // private
18056     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18057     onTriggerClick : function(){
18058         if(this.disabled){
18059             return;
18060         }
18061         if(this.menu == null){
18062             this.menu = new Roo.menu.DateMenu();
18063         }
18064         Roo.apply(this.menu.picker,  {
18065             showClear: this.allowBlank,
18066             minDate : this.minValue,
18067             maxDate : this.maxValue,
18068             disabledDatesRE : this.ddMatch,
18069             disabledDatesText : this.disabledDatesText,
18070             disabledDays : this.disabledDays,
18071             disabledDaysText : this.disabledDaysText,
18072             format : this.useIso ? 'Y-m-d' : this.format,
18073             minText : String.format(this.minText, this.formatDate(this.minValue)),
18074             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18075         });
18076         this.menu.on(Roo.apply({}, this.menuListeners, {
18077             scope:this
18078         }));
18079         this.menu.picker.setValue(this.getValue() || new Date());
18080         this.menu.show(this.el, "tl-bl?");
18081     },
18082
18083     beforeBlur : function(){
18084         var v = this.parseDate(this.getRawValue());
18085         if(v){
18086             this.setValue(v);
18087         }
18088     },
18089
18090     /*@
18091      * overide
18092      * 
18093      */
18094     isDirty : function() {
18095         if(this.disabled) {
18096             return false;
18097         }
18098         
18099         if(typeof(this.startValue) === 'undefined'){
18100             return false;
18101         }
18102         
18103         return String(this.getValue()) !== String(this.startValue);
18104         
18105     }
18106 });/*
18107  * Based on:
18108  * Ext JS Library 1.1.1
18109  * Copyright(c) 2006-2007, Ext JS, LLC.
18110  *
18111  * Originally Released Under LGPL - original licence link has changed is not relivant.
18112  *
18113  * Fork - LGPL
18114  * <script type="text/javascript">
18115  */
18116  
18117 /**
18118  * @class Roo.form.MonthField
18119  * @extends Roo.form.TriggerField
18120  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18121 * @constructor
18122 * Create a new MonthField
18123 * @param {Object} config
18124  */
18125 Roo.form.MonthField = function(config){
18126     
18127     Roo.form.MonthField.superclass.constructor.call(this, config);
18128     
18129       this.addEvents({
18130          
18131         /**
18132          * @event select
18133          * Fires when a date is selected
18134              * @param {Roo.form.MonthFieeld} combo This combo box
18135              * @param {Date} date The date selected
18136              */
18137         'select' : true
18138          
18139     });
18140     
18141     
18142     if(typeof this.minValue == "string") {
18143         this.minValue = this.parseDate(this.minValue);
18144     }
18145     if(typeof this.maxValue == "string") {
18146         this.maxValue = this.parseDate(this.maxValue);
18147     }
18148     this.ddMatch = null;
18149     if(this.disabledDates){
18150         var dd = this.disabledDates;
18151         var re = "(?:";
18152         for(var i = 0; i < dd.length; i++){
18153             re += dd[i];
18154             if(i != dd.length-1) {
18155                 re += "|";
18156             }
18157         }
18158         this.ddMatch = new RegExp(re + ")");
18159     }
18160 };
18161
18162 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18163     /**
18164      * @cfg {String} format
18165      * The default date format string which can be overriden for localization support.  The format must be
18166      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18167      */
18168     format : "M Y",
18169     /**
18170      * @cfg {String} altFormats
18171      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18172      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18173      */
18174     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18175     /**
18176      * @cfg {Array} disabledDays
18177      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18178      */
18179     disabledDays : [0,1,2,3,4,5,6],
18180     /**
18181      * @cfg {String} disabledDaysText
18182      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18183      */
18184     disabledDaysText : "Disabled",
18185     /**
18186      * @cfg {Array} disabledDates
18187      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18188      * expression so they are very powerful. Some examples:
18189      * <ul>
18190      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18191      * <li>["03/08", "09/16"] would disable those days for every year</li>
18192      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18193      * <li>["03/../2006"] would disable every day in March 2006</li>
18194      * <li>["^03"] would disable every day in every March</li>
18195      * </ul>
18196      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18197      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18198      */
18199     disabledDates : null,
18200     /**
18201      * @cfg {String} disabledDatesText
18202      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18203      */
18204     disabledDatesText : "Disabled",
18205     /**
18206      * @cfg {Date/String} minValue
18207      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18208      * valid format (defaults to null).
18209      */
18210     minValue : null,
18211     /**
18212      * @cfg {Date/String} maxValue
18213      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18214      * valid format (defaults to null).
18215      */
18216     maxValue : null,
18217     /**
18218      * @cfg {String} minText
18219      * The error text to display when the date in the cell is before minValue (defaults to
18220      * 'The date in this field must be after {minValue}').
18221      */
18222     minText : "The date in this field must be equal to or after {0}",
18223     /**
18224      * @cfg {String} maxTextf
18225      * The error text to display when the date in the cell is after maxValue (defaults to
18226      * 'The date in this field must be before {maxValue}').
18227      */
18228     maxText : "The date in this field must be equal to or before {0}",
18229     /**
18230      * @cfg {String} invalidText
18231      * The error text to display when the date in the field is invalid (defaults to
18232      * '{value} is not a valid date - it must be in the format {format}').
18233      */
18234     invalidText : "{0} is not a valid date - it must be in the format {1}",
18235     /**
18236      * @cfg {String} triggerClass
18237      * An additional CSS class used to style the trigger button.  The trigger will always get the
18238      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18239      * which displays a calendar icon).
18240      */
18241     triggerClass : 'x-form-date-trigger',
18242     
18243
18244     /**
18245      * @cfg {Boolean} useIso
18246      * if enabled, then the date field will use a hidden field to store the 
18247      * real value as iso formated date. default (true)
18248      */ 
18249     useIso : true,
18250     /**
18251      * @cfg {String/Object} autoCreate
18252      * A DomHelper element spec, or true for a default element spec (defaults to
18253      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18254      */ 
18255     // private
18256     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18257     
18258     // private
18259     hiddenField: false,
18260     
18261     hideMonthPicker : false,
18262     
18263     onRender : function(ct, position)
18264     {
18265         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18266         if (this.useIso) {
18267             this.el.dom.removeAttribute('name'); 
18268             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18269                     'before', true);
18270             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18271             // prevent input submission
18272             this.hiddenName = this.name;
18273         }
18274             
18275             
18276     },
18277     
18278     // private
18279     validateValue : function(value)
18280     {
18281         value = this.formatDate(value);
18282         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18283             return false;
18284         }
18285         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18286              return true;
18287         }
18288         var svalue = value;
18289         value = this.parseDate(value);
18290         if(!value){
18291             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18292             return false;
18293         }
18294         var time = value.getTime();
18295         if(this.minValue && time < this.minValue.getTime()){
18296             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18297             return false;
18298         }
18299         if(this.maxValue && time > this.maxValue.getTime()){
18300             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18301             return false;
18302         }
18303         /*if(this.disabledDays){
18304             var day = value.getDay();
18305             for(var i = 0; i < this.disabledDays.length; i++) {
18306                 if(day === this.disabledDays[i]){
18307                     this.markInvalid(this.disabledDaysText);
18308                     return false;
18309                 }
18310             }
18311         }
18312         */
18313         var fvalue = this.formatDate(value);
18314         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18315             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18316             return false;
18317         }
18318         */
18319         return true;
18320     },
18321
18322     // private
18323     // Provides logic to override the default TriggerField.validateBlur which just returns true
18324     validateBlur : function(){
18325         return !this.menu || !this.menu.isVisible();
18326     },
18327
18328     /**
18329      * Returns the current date value of the date field.
18330      * @return {Date} The date value
18331      */
18332     getValue : function(){
18333         
18334         
18335         
18336         return  this.hiddenField ?
18337                 this.hiddenField.value :
18338                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18339     },
18340
18341     /**
18342      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18343      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18344      * (the default format used is "m/d/y").
18345      * <br />Usage:
18346      * <pre><code>
18347 //All of these calls set the same date value (May 4, 2006)
18348
18349 //Pass a date object:
18350 var dt = new Date('5/4/06');
18351 monthField.setValue(dt);
18352
18353 //Pass a date string (default format):
18354 monthField.setValue('5/4/06');
18355
18356 //Pass a date string (custom format):
18357 monthField.format = 'Y-m-d';
18358 monthField.setValue('2006-5-4');
18359 </code></pre>
18360      * @param {String/Date} date The date or valid date string
18361      */
18362     setValue : function(date){
18363         Roo.log('month setValue' + date);
18364         // can only be first of month..
18365         
18366         var val = this.parseDate(date);
18367         
18368         if (this.hiddenField) {
18369             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18370         }
18371         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18372         this.value = this.parseDate(date);
18373     },
18374
18375     // private
18376     parseDate : function(value){
18377         if(!value || value instanceof Date){
18378             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18379             return value;
18380         }
18381         var v = Date.parseDate(value, this.format);
18382         if (!v && this.useIso) {
18383             v = Date.parseDate(value, 'Y-m-d');
18384         }
18385         if (v) {
18386             // 
18387             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18388         }
18389         
18390         
18391         if(!v && this.altFormats){
18392             if(!this.altFormatsArray){
18393                 this.altFormatsArray = this.altFormats.split("|");
18394             }
18395             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18396                 v = Date.parseDate(value, this.altFormatsArray[i]);
18397             }
18398         }
18399         return v;
18400     },
18401
18402     // private
18403     formatDate : function(date, fmt){
18404         return (!date || !(date instanceof Date)) ?
18405                date : date.dateFormat(fmt || this.format);
18406     },
18407
18408     // private
18409     menuListeners : {
18410         select: function(m, d){
18411             this.setValue(d);
18412             this.fireEvent('select', this, d);
18413         },
18414         show : function(){ // retain focus styling
18415             this.onFocus();
18416         },
18417         hide : function(){
18418             this.focus.defer(10, this);
18419             var ml = this.menuListeners;
18420             this.menu.un("select", ml.select,  this);
18421             this.menu.un("show", ml.show,  this);
18422             this.menu.un("hide", ml.hide,  this);
18423         }
18424     },
18425     // private
18426     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18427     onTriggerClick : function(){
18428         if(this.disabled){
18429             return;
18430         }
18431         if(this.menu == null){
18432             this.menu = new Roo.menu.DateMenu();
18433            
18434         }
18435         
18436         Roo.apply(this.menu.picker,  {
18437             
18438             showClear: this.allowBlank,
18439             minDate : this.minValue,
18440             maxDate : this.maxValue,
18441             disabledDatesRE : this.ddMatch,
18442             disabledDatesText : this.disabledDatesText,
18443             
18444             format : this.useIso ? 'Y-m-d' : this.format,
18445             minText : String.format(this.minText, this.formatDate(this.minValue)),
18446             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18447             
18448         });
18449          this.menu.on(Roo.apply({}, this.menuListeners, {
18450             scope:this
18451         }));
18452        
18453         
18454         var m = this.menu;
18455         var p = m.picker;
18456         
18457         // hide month picker get's called when we called by 'before hide';
18458         
18459         var ignorehide = true;
18460         p.hideMonthPicker  = function(disableAnim){
18461             if (ignorehide) {
18462                 return;
18463             }
18464              if(this.monthPicker){
18465                 Roo.log("hideMonthPicker called");
18466                 if(disableAnim === true){
18467                     this.monthPicker.hide();
18468                 }else{
18469                     this.monthPicker.slideOut('t', {duration:.2});
18470                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18471                     p.fireEvent("select", this, this.value);
18472                     m.hide();
18473                 }
18474             }
18475         }
18476         
18477         Roo.log('picker set value');
18478         Roo.log(this.getValue());
18479         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18480         m.show(this.el, 'tl-bl?');
18481         ignorehide  = false;
18482         // this will trigger hideMonthPicker..
18483         
18484         
18485         // hidden the day picker
18486         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18487         
18488         
18489         
18490       
18491         
18492         p.showMonthPicker.defer(100, p);
18493     
18494         
18495        
18496     },
18497
18498     beforeBlur : function(){
18499         var v = this.parseDate(this.getRawValue());
18500         if(v){
18501             this.setValue(v);
18502         }
18503     }
18504
18505     /** @cfg {Boolean} grow @hide */
18506     /** @cfg {Number} growMin @hide */
18507     /** @cfg {Number} growMax @hide */
18508     /**
18509      * @hide
18510      * @method autoSize
18511      */
18512 });/*
18513  * Based on:
18514  * Ext JS Library 1.1.1
18515  * Copyright(c) 2006-2007, Ext JS, LLC.
18516  *
18517  * Originally Released Under LGPL - original licence link has changed is not relivant.
18518  *
18519  * Fork - LGPL
18520  * <script type="text/javascript">
18521  */
18522  
18523
18524 /**
18525  * @class Roo.form.ComboBox
18526  * @extends Roo.form.TriggerField
18527  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18528  * @constructor
18529  * Create a new ComboBox.
18530  * @param {Object} config Configuration options
18531  */
18532 Roo.form.ComboBox = function(config){
18533     Roo.form.ComboBox.superclass.constructor.call(this, config);
18534     this.addEvents({
18535         /**
18536          * @event expand
18537          * Fires when the dropdown list is expanded
18538              * @param {Roo.form.ComboBox} combo This combo box
18539              */
18540         'expand' : true,
18541         /**
18542          * @event collapse
18543          * Fires when the dropdown list is collapsed
18544              * @param {Roo.form.ComboBox} combo This combo box
18545              */
18546         'collapse' : true,
18547         /**
18548          * @event beforeselect
18549          * Fires before a list item is selected. Return false to cancel the selection.
18550              * @param {Roo.form.ComboBox} combo This combo box
18551              * @param {Roo.data.Record} record The data record returned from the underlying store
18552              * @param {Number} index The index of the selected item in the dropdown list
18553              */
18554         'beforeselect' : true,
18555         /**
18556          * @event select
18557          * Fires when a list item is selected
18558              * @param {Roo.form.ComboBox} combo This combo box
18559              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18560              * @param {Number} index The index of the selected item in the dropdown list
18561              */
18562         'select' : true,
18563         /**
18564          * @event beforequery
18565          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18566          * The event object passed has these properties:
18567              * @param {Roo.form.ComboBox} combo This combo box
18568              * @param {String} query The query
18569              * @param {Boolean} forceAll true to force "all" query
18570              * @param {Boolean} cancel true to cancel the query
18571              * @param {Object} e The query event object
18572              */
18573         'beforequery': true,
18574          /**
18575          * @event add
18576          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18577              * @param {Roo.form.ComboBox} combo This combo box
18578              */
18579         'add' : true,
18580         /**
18581          * @event edit
18582          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18583              * @param {Roo.form.ComboBox} combo This combo box
18584              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18585              */
18586         'edit' : true
18587         
18588         
18589     });
18590     if(this.transform){
18591         this.allowDomMove = false;
18592         var s = Roo.getDom(this.transform);
18593         if(!this.hiddenName){
18594             this.hiddenName = s.name;
18595         }
18596         if(!this.store){
18597             this.mode = 'local';
18598             var d = [], opts = s.options;
18599             for(var i = 0, len = opts.length;i < len; i++){
18600                 var o = opts[i];
18601                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18602                 if(o.selected) {
18603                     this.value = value;
18604                 }
18605                 d.push([value, o.text]);
18606             }
18607             this.store = new Roo.data.SimpleStore({
18608                 'id': 0,
18609                 fields: ['value', 'text'],
18610                 data : d
18611             });
18612             this.valueField = 'value';
18613             this.displayField = 'text';
18614         }
18615         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18616         if(!this.lazyRender){
18617             this.target = true;
18618             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18619             s.parentNode.removeChild(s); // remove it
18620             this.render(this.el.parentNode);
18621         }else{
18622             s.parentNode.removeChild(s); // remove it
18623         }
18624
18625     }
18626     if (this.store) {
18627         this.store = Roo.factory(this.store, Roo.data);
18628     }
18629     
18630     this.selectedIndex = -1;
18631     if(this.mode == 'local'){
18632         if(config.queryDelay === undefined){
18633             this.queryDelay = 10;
18634         }
18635         if(config.minChars === undefined){
18636             this.minChars = 0;
18637         }
18638     }
18639 };
18640
18641 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18642     /**
18643      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18644      */
18645     /**
18646      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18647      * rendering into an Roo.Editor, defaults to false)
18648      */
18649     /**
18650      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18651      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18652      */
18653     /**
18654      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18655      */
18656     /**
18657      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18658      * the dropdown list (defaults to undefined, with no header element)
18659      */
18660
18661      /**
18662      * @cfg {String/Roo.Template} tpl The template to use to render the output
18663      */
18664      
18665     // private
18666     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18667     /**
18668      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18669      */
18670     listWidth: undefined,
18671     /**
18672      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18673      * mode = 'remote' or 'text' if mode = 'local')
18674      */
18675     displayField: undefined,
18676     /**
18677      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18678      * mode = 'remote' or 'value' if mode = 'local'). 
18679      * Note: use of a valueField requires the user make a selection
18680      * in order for a value to be mapped.
18681      */
18682     valueField: undefined,
18683     
18684     
18685     /**
18686      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18687      * field's data value (defaults to the underlying DOM element's name)
18688      */
18689     hiddenName: undefined,
18690     /**
18691      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18692      */
18693     listClass: '',
18694     /**
18695      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18696      */
18697     selectedClass: 'x-combo-selected',
18698     /**
18699      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18700      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18701      * which displays a downward arrow icon).
18702      */
18703     triggerClass : 'x-form-arrow-trigger',
18704     /**
18705      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18706      */
18707     shadow:'sides',
18708     /**
18709      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18710      * anchor positions (defaults to 'tl-bl')
18711      */
18712     listAlign: 'tl-bl?',
18713     /**
18714      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18715      */
18716     maxHeight: 300,
18717     /**
18718      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18719      * query specified by the allQuery config option (defaults to 'query')
18720      */
18721     triggerAction: 'query',
18722     /**
18723      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18724      * (defaults to 4, does not apply if editable = false)
18725      */
18726     minChars : 4,
18727     /**
18728      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18729      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18730      */
18731     typeAhead: false,
18732     /**
18733      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18734      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18735      */
18736     queryDelay: 500,
18737     /**
18738      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18739      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18740      */
18741     pageSize: 0,
18742     /**
18743      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18744      * when editable = true (defaults to false)
18745      */
18746     selectOnFocus:false,
18747     /**
18748      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18749      */
18750     queryParam: 'query',
18751     /**
18752      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18753      * when mode = 'remote' (defaults to 'Loading...')
18754      */
18755     loadingText: 'Loading...',
18756     /**
18757      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18758      */
18759     resizable: false,
18760     /**
18761      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18762      */
18763     handleHeight : 8,
18764     /**
18765      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18766      * traditional select (defaults to true)
18767      */
18768     editable: true,
18769     /**
18770      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18771      */
18772     allQuery: '',
18773     /**
18774      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18775      */
18776     mode: 'remote',
18777     /**
18778      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18779      * listWidth has a higher value)
18780      */
18781     minListWidth : 70,
18782     /**
18783      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18784      * allow the user to set arbitrary text into the field (defaults to false)
18785      */
18786     forceSelection:false,
18787     /**
18788      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18789      * if typeAhead = true (defaults to 250)
18790      */
18791     typeAheadDelay : 250,
18792     /**
18793      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18794      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18795      */
18796     valueNotFoundText : undefined,
18797     /**
18798      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18799      */
18800     blockFocus : false,
18801     
18802     /**
18803      * @cfg {Boolean} disableClear Disable showing of clear button.
18804      */
18805     disableClear : false,
18806     /**
18807      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18808      */
18809     alwaysQuery : false,
18810     
18811     //private
18812     addicon : false,
18813     editicon: false,
18814     
18815     // element that contains real text value.. (when hidden is used..)
18816      
18817     // private
18818     onRender : function(ct, position){
18819         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18820         if(this.hiddenName){
18821             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18822                     'before', true);
18823             this.hiddenField.value =
18824                 this.hiddenValue !== undefined ? this.hiddenValue :
18825                 this.value !== undefined ? this.value : '';
18826
18827             // prevent input submission
18828             this.el.dom.removeAttribute('name');
18829              
18830              
18831         }
18832         if(Roo.isGecko){
18833             this.el.dom.setAttribute('autocomplete', 'off');
18834         }
18835
18836         var cls = 'x-combo-list';
18837
18838         this.list = new Roo.Layer({
18839             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18840         });
18841
18842         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18843         this.list.setWidth(lw);
18844         this.list.swallowEvent('mousewheel');
18845         this.assetHeight = 0;
18846
18847         if(this.title){
18848             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18849             this.assetHeight += this.header.getHeight();
18850         }
18851
18852         this.innerList = this.list.createChild({cls:cls+'-inner'});
18853         this.innerList.on('mouseover', this.onViewOver, this);
18854         this.innerList.on('mousemove', this.onViewMove, this);
18855         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18856         
18857         if(this.allowBlank && !this.pageSize && !this.disableClear){
18858             this.footer = this.list.createChild({cls:cls+'-ft'});
18859             this.pageTb = new Roo.Toolbar(this.footer);
18860            
18861         }
18862         if(this.pageSize){
18863             this.footer = this.list.createChild({cls:cls+'-ft'});
18864             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18865                     {pageSize: this.pageSize});
18866             
18867         }
18868         
18869         if (this.pageTb && this.allowBlank && !this.disableClear) {
18870             var _this = this;
18871             this.pageTb.add(new Roo.Toolbar.Fill(), {
18872                 cls: 'x-btn-icon x-btn-clear',
18873                 text: '&#160;',
18874                 handler: function()
18875                 {
18876                     _this.collapse();
18877                     _this.clearValue();
18878                     _this.onSelect(false, -1);
18879                 }
18880             });
18881         }
18882         if (this.footer) {
18883             this.assetHeight += this.footer.getHeight();
18884         }
18885         
18886
18887         if(!this.tpl){
18888             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18889         }
18890
18891         this.view = new Roo.View(this.innerList, this.tpl, {
18892             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18893         });
18894
18895         this.view.on('click', this.onViewClick, this);
18896
18897         this.store.on('beforeload', this.onBeforeLoad, this);
18898         this.store.on('load', this.onLoad, this);
18899         this.store.on('loadexception', this.onLoadException, this);
18900
18901         if(this.resizable){
18902             this.resizer = new Roo.Resizable(this.list,  {
18903                pinned:true, handles:'se'
18904             });
18905             this.resizer.on('resize', function(r, w, h){
18906                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18907                 this.listWidth = w;
18908                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18909                 this.restrictHeight();
18910             }, this);
18911             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18912         }
18913         if(!this.editable){
18914             this.editable = true;
18915             this.setEditable(false);
18916         }  
18917         
18918         
18919         if (typeof(this.events.add.listeners) != 'undefined') {
18920             
18921             this.addicon = this.wrap.createChild(
18922                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18923        
18924             this.addicon.on('click', function(e) {
18925                 this.fireEvent('add', this);
18926             }, this);
18927         }
18928         if (typeof(this.events.edit.listeners) != 'undefined') {
18929             
18930             this.editicon = this.wrap.createChild(
18931                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18932             if (this.addicon) {
18933                 this.editicon.setStyle('margin-left', '40px');
18934             }
18935             this.editicon.on('click', function(e) {
18936                 
18937                 // we fire even  if inothing is selected..
18938                 this.fireEvent('edit', this, this.lastData );
18939                 
18940             }, this);
18941         }
18942         
18943         
18944         
18945     },
18946
18947     // private
18948     initEvents : function(){
18949         Roo.form.ComboBox.superclass.initEvents.call(this);
18950
18951         this.keyNav = new Roo.KeyNav(this.el, {
18952             "up" : function(e){
18953                 this.inKeyMode = true;
18954                 this.selectPrev();
18955             },
18956
18957             "down" : function(e){
18958                 if(!this.isExpanded()){
18959                     this.onTriggerClick();
18960                 }else{
18961                     this.inKeyMode = true;
18962                     this.selectNext();
18963                 }
18964             },
18965
18966             "enter" : function(e){
18967                 this.onViewClick();
18968                 //return true;
18969             },
18970
18971             "esc" : function(e){
18972                 this.collapse();
18973             },
18974
18975             "tab" : function(e){
18976                 this.onViewClick(false);
18977                 this.fireEvent("specialkey", this, e);
18978                 return true;
18979             },
18980
18981             scope : this,
18982
18983             doRelay : function(foo, bar, hname){
18984                 if(hname == 'down' || this.scope.isExpanded()){
18985                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18986                 }
18987                 return true;
18988             },
18989
18990             forceKeyDown: true
18991         });
18992         this.queryDelay = Math.max(this.queryDelay || 10,
18993                 this.mode == 'local' ? 10 : 250);
18994         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18995         if(this.typeAhead){
18996             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18997         }
18998         if(this.editable !== false){
18999             this.el.on("keyup", this.onKeyUp, this);
19000         }
19001         if(this.forceSelection){
19002             this.on('blur', this.doForce, this);
19003         }
19004     },
19005
19006     onDestroy : function(){
19007         if(this.view){
19008             this.view.setStore(null);
19009             this.view.el.removeAllListeners();
19010             this.view.el.remove();
19011             this.view.purgeListeners();
19012         }
19013         if(this.list){
19014             this.list.destroy();
19015         }
19016         if(this.store){
19017             this.store.un('beforeload', this.onBeforeLoad, this);
19018             this.store.un('load', this.onLoad, this);
19019             this.store.un('loadexception', this.onLoadException, this);
19020         }
19021         Roo.form.ComboBox.superclass.onDestroy.call(this);
19022     },
19023
19024     // private
19025     fireKey : function(e){
19026         if(e.isNavKeyPress() && !this.list.isVisible()){
19027             this.fireEvent("specialkey", this, e);
19028         }
19029     },
19030
19031     // private
19032     onResize: function(w, h){
19033         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19034         
19035         if(typeof w != 'number'){
19036             // we do not handle it!?!?
19037             return;
19038         }
19039         var tw = this.trigger.getWidth();
19040         tw += this.addicon ? this.addicon.getWidth() : 0;
19041         tw += this.editicon ? this.editicon.getWidth() : 0;
19042         var x = w - tw;
19043         this.el.setWidth( this.adjustWidth('input', x));
19044             
19045         this.trigger.setStyle('left', x+'px');
19046         
19047         if(this.list && this.listWidth === undefined){
19048             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19049             this.list.setWidth(lw);
19050             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19051         }
19052         
19053     
19054         
19055     },
19056
19057     /**
19058      * Allow or prevent the user from directly editing the field text.  If false is passed,
19059      * the user will only be able to select from the items defined in the dropdown list.  This method
19060      * is the runtime equivalent of setting the 'editable' config option at config time.
19061      * @param {Boolean} value True to allow the user to directly edit the field text
19062      */
19063     setEditable : function(value){
19064         if(value == this.editable){
19065             return;
19066         }
19067         this.editable = value;
19068         if(!value){
19069             this.el.dom.setAttribute('readOnly', true);
19070             this.el.on('mousedown', this.onTriggerClick,  this);
19071             this.el.addClass('x-combo-noedit');
19072         }else{
19073             this.el.dom.setAttribute('readOnly', false);
19074             this.el.un('mousedown', this.onTriggerClick,  this);
19075             this.el.removeClass('x-combo-noedit');
19076         }
19077     },
19078
19079     // private
19080     onBeforeLoad : function(){
19081         if(!this.hasFocus){
19082             return;
19083         }
19084         this.innerList.update(this.loadingText ?
19085                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19086         this.restrictHeight();
19087         this.selectedIndex = -1;
19088     },
19089
19090     // private
19091     onLoad : function(){
19092         if(!this.hasFocus){
19093             return;
19094         }
19095         if(this.store.getCount() > 0){
19096             this.expand();
19097             this.restrictHeight();
19098             if(this.lastQuery == this.allQuery){
19099                 if(this.editable){
19100                     this.el.dom.select();
19101                 }
19102                 if(!this.selectByValue(this.value, true)){
19103                     this.select(0, true);
19104                 }
19105             }else{
19106                 this.selectNext();
19107                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19108                     this.taTask.delay(this.typeAheadDelay);
19109                 }
19110             }
19111         }else{
19112             this.onEmptyResults();
19113         }
19114         //this.el.focus();
19115     },
19116     // private
19117     onLoadException : function()
19118     {
19119         this.collapse();
19120         Roo.log(this.store.reader.jsonData);
19121         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19122             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19123         }
19124         
19125         
19126     },
19127     // private
19128     onTypeAhead : function(){
19129         if(this.store.getCount() > 0){
19130             var r = this.store.getAt(0);
19131             var newValue = r.data[this.displayField];
19132             var len = newValue.length;
19133             var selStart = this.getRawValue().length;
19134             if(selStart != len){
19135                 this.setRawValue(newValue);
19136                 this.selectText(selStart, newValue.length);
19137             }
19138         }
19139     },
19140
19141     // private
19142     onSelect : function(record, index){
19143         if(this.fireEvent('beforeselect', this, record, index) !== false){
19144             this.setFromData(index > -1 ? record.data : false);
19145             this.collapse();
19146             this.fireEvent('select', this, record, index);
19147         }
19148     },
19149
19150     /**
19151      * Returns the currently selected field value or empty string if no value is set.
19152      * @return {String} value The selected value
19153      */
19154     getValue : function(){
19155         if(this.valueField){
19156             return typeof this.value != 'undefined' ? this.value : '';
19157         }
19158         return Roo.form.ComboBox.superclass.getValue.call(this);
19159     },
19160
19161     /**
19162      * Clears any text/value currently set in the field
19163      */
19164     clearValue : function(){
19165         if(this.hiddenField){
19166             this.hiddenField.value = '';
19167         }
19168         this.value = '';
19169         this.setRawValue('');
19170         this.lastSelectionText = '';
19171         
19172     },
19173
19174     /**
19175      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19176      * will be displayed in the field.  If the value does not match the data value of an existing item,
19177      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19178      * Otherwise the field will be blank (although the value will still be set).
19179      * @param {String} value The value to match
19180      */
19181     setValue : function(v){
19182         var text = v;
19183         if(this.valueField){
19184             var r = this.findRecord(this.valueField, v);
19185             if(r){
19186                 text = r.data[this.displayField];
19187             }else if(this.valueNotFoundText !== undefined){
19188                 text = this.valueNotFoundText;
19189             }
19190         }
19191         this.lastSelectionText = text;
19192         if(this.hiddenField){
19193             this.hiddenField.value = v;
19194         }
19195         Roo.form.ComboBox.superclass.setValue.call(this, text);
19196         this.value = v;
19197     },
19198     /**
19199      * @property {Object} the last set data for the element
19200      */
19201     
19202     lastData : false,
19203     /**
19204      * Sets the value of the field based on a object which is related to the record format for the store.
19205      * @param {Object} value the value to set as. or false on reset?
19206      */
19207     setFromData : function(o){
19208         var dv = ''; // display value
19209         var vv = ''; // value value..
19210         this.lastData = o;
19211         if (this.displayField) {
19212             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19213         } else {
19214             // this is an error condition!!!
19215             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19216         }
19217         
19218         if(this.valueField){
19219             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19220         }
19221         if(this.hiddenField){
19222             this.hiddenField.value = vv;
19223             
19224             this.lastSelectionText = dv;
19225             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19226             this.value = vv;
19227             return;
19228         }
19229         // no hidden field.. - we store the value in 'value', but still display
19230         // display field!!!!
19231         this.lastSelectionText = dv;
19232         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19233         this.value = vv;
19234         
19235         
19236     },
19237     // private
19238     reset : function(){
19239         // overridden so that last data is reset..
19240         this.setValue(this.resetValue);
19241         this.originalValue = this.getValue();
19242         this.clearInvalid();
19243         this.lastData = false;
19244         if (this.view) {
19245             this.view.clearSelections();
19246         }
19247     },
19248     // private
19249     findRecord : function(prop, value){
19250         var record;
19251         if(this.store.getCount() > 0){
19252             this.store.each(function(r){
19253                 if(r.data[prop] == value){
19254                     record = r;
19255                     return false;
19256                 }
19257                 return true;
19258             });
19259         }
19260         return record;
19261     },
19262     
19263     getName: function()
19264     {
19265         // returns hidden if it's set..
19266         if (!this.rendered) {return ''};
19267         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19268         
19269     },
19270     // private
19271     onViewMove : function(e, t){
19272         this.inKeyMode = false;
19273     },
19274
19275     // private
19276     onViewOver : function(e, t){
19277         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19278             return;
19279         }
19280         var item = this.view.findItemFromChild(t);
19281         if(item){
19282             var index = this.view.indexOf(item);
19283             this.select(index, false);
19284         }
19285     },
19286
19287     // private
19288     onViewClick : function(doFocus)
19289     {
19290         var index = this.view.getSelectedIndexes()[0];
19291         var r = this.store.getAt(index);
19292         if(r){
19293             this.onSelect(r, index);
19294         }
19295         if(doFocus !== false && !this.blockFocus){
19296             this.el.focus();
19297         }
19298     },
19299
19300     // private
19301     restrictHeight : function(){
19302         this.innerList.dom.style.height = '';
19303         var inner = this.innerList.dom;
19304         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19305         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19306         this.list.beginUpdate();
19307         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19308         this.list.alignTo(this.el, this.listAlign);
19309         this.list.endUpdate();
19310     },
19311
19312     // private
19313     onEmptyResults : function(){
19314         this.collapse();
19315     },
19316
19317     /**
19318      * Returns true if the dropdown list is expanded, else false.
19319      */
19320     isExpanded : function(){
19321         return this.list.isVisible();
19322     },
19323
19324     /**
19325      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19326      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19327      * @param {String} value The data value of the item to select
19328      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19329      * selected item if it is not currently in view (defaults to true)
19330      * @return {Boolean} True if the value matched an item in the list, else false
19331      */
19332     selectByValue : function(v, scrollIntoView){
19333         if(v !== undefined && v !== null){
19334             var r = this.findRecord(this.valueField || this.displayField, v);
19335             if(r){
19336                 this.select(this.store.indexOf(r), scrollIntoView);
19337                 return true;
19338             }
19339         }
19340         return false;
19341     },
19342
19343     /**
19344      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19345      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19346      * @param {Number} index The zero-based index of the list item to select
19347      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19348      * selected item if it is not currently in view (defaults to true)
19349      */
19350     select : function(index, scrollIntoView){
19351         this.selectedIndex = index;
19352         this.view.select(index);
19353         if(scrollIntoView !== false){
19354             var el = this.view.getNode(index);
19355             if(el){
19356                 this.innerList.scrollChildIntoView(el, false);
19357             }
19358         }
19359     },
19360
19361     // private
19362     selectNext : function(){
19363         var ct = this.store.getCount();
19364         if(ct > 0){
19365             if(this.selectedIndex == -1){
19366                 this.select(0);
19367             }else if(this.selectedIndex < ct-1){
19368                 this.select(this.selectedIndex+1);
19369             }
19370         }
19371     },
19372
19373     // private
19374     selectPrev : function(){
19375         var ct = this.store.getCount();
19376         if(ct > 0){
19377             if(this.selectedIndex == -1){
19378                 this.select(0);
19379             }else if(this.selectedIndex != 0){
19380                 this.select(this.selectedIndex-1);
19381             }
19382         }
19383     },
19384
19385     // private
19386     onKeyUp : function(e){
19387         if(this.editable !== false && !e.isSpecialKey()){
19388             this.lastKey = e.getKey();
19389             this.dqTask.delay(this.queryDelay);
19390         }
19391     },
19392
19393     // private
19394     validateBlur : function(){
19395         return !this.list || !this.list.isVisible();   
19396     },
19397
19398     // private
19399     initQuery : function(){
19400         this.doQuery(this.getRawValue());
19401     },
19402
19403     // private
19404     doForce : function(){
19405         if(this.el.dom.value.length > 0){
19406             this.el.dom.value =
19407                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19408              
19409         }
19410     },
19411
19412     /**
19413      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19414      * query allowing the query action to be canceled if needed.
19415      * @param {String} query The SQL query to execute
19416      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19417      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19418      * saved in the current store (defaults to false)
19419      */
19420     doQuery : function(q, forceAll){
19421         if(q === undefined || q === null){
19422             q = '';
19423         }
19424         var qe = {
19425             query: q,
19426             forceAll: forceAll,
19427             combo: this,
19428             cancel:false
19429         };
19430         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19431             return false;
19432         }
19433         q = qe.query;
19434         forceAll = qe.forceAll;
19435         if(forceAll === true || (q.length >= this.minChars)){
19436             if(this.lastQuery != q || this.alwaysQuery){
19437                 this.lastQuery = q;
19438                 if(this.mode == 'local'){
19439                     this.selectedIndex = -1;
19440                     if(forceAll){
19441                         this.store.clearFilter();
19442                     }else{
19443                         this.store.filter(this.displayField, q);
19444                     }
19445                     this.onLoad();
19446                 }else{
19447                     this.store.baseParams[this.queryParam] = q;
19448                     this.store.load({
19449                         params: this.getParams(q)
19450                     });
19451                     this.expand();
19452                 }
19453             }else{
19454                 this.selectedIndex = -1;
19455                 this.onLoad();   
19456             }
19457         }
19458     },
19459
19460     // private
19461     getParams : function(q){
19462         var p = {};
19463         //p[this.queryParam] = q;
19464         if(this.pageSize){
19465             p.start = 0;
19466             p.limit = this.pageSize;
19467         }
19468         return p;
19469     },
19470
19471     /**
19472      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19473      */
19474     collapse : function(){
19475         if(!this.isExpanded()){
19476             return;
19477         }
19478         this.list.hide();
19479         Roo.get(document).un('mousedown', this.collapseIf, this);
19480         Roo.get(document).un('mousewheel', this.collapseIf, this);
19481         if (!this.editable) {
19482             Roo.get(document).un('keydown', this.listKeyPress, this);
19483         }
19484         this.fireEvent('collapse', this);
19485     },
19486
19487     // private
19488     collapseIf : function(e){
19489         if(!e.within(this.wrap) && !e.within(this.list)){
19490             this.collapse();
19491         }
19492     },
19493
19494     /**
19495      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19496      */
19497     expand : function(){
19498         if(this.isExpanded() || !this.hasFocus){
19499             return;
19500         }
19501         this.list.alignTo(this.el, this.listAlign);
19502         this.list.show();
19503         Roo.get(document).on('mousedown', this.collapseIf, this);
19504         Roo.get(document).on('mousewheel', this.collapseIf, this);
19505         if (!this.editable) {
19506             Roo.get(document).on('keydown', this.listKeyPress, this);
19507         }
19508         
19509         this.fireEvent('expand', this);
19510     },
19511
19512     // private
19513     // Implements the default empty TriggerField.onTriggerClick function
19514     onTriggerClick : function(){
19515         if(this.disabled){
19516             return;
19517         }
19518         if(this.isExpanded()){
19519             this.collapse();
19520             if (!this.blockFocus) {
19521                 this.el.focus();
19522             }
19523             
19524         }else {
19525             this.hasFocus = true;
19526             if(this.triggerAction == 'all') {
19527                 this.doQuery(this.allQuery, true);
19528             } else {
19529                 this.doQuery(this.getRawValue());
19530             }
19531             if (!this.blockFocus) {
19532                 this.el.focus();
19533             }
19534         }
19535     },
19536     listKeyPress : function(e)
19537     {
19538         //Roo.log('listkeypress');
19539         // scroll to first matching element based on key pres..
19540         if (e.isSpecialKey()) {
19541             return false;
19542         }
19543         var k = String.fromCharCode(e.getKey()).toUpperCase();
19544         //Roo.log(k);
19545         var match  = false;
19546         var csel = this.view.getSelectedNodes();
19547         var cselitem = false;
19548         if (csel.length) {
19549             var ix = this.view.indexOf(csel[0]);
19550             cselitem  = this.store.getAt(ix);
19551             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19552                 cselitem = false;
19553             }
19554             
19555         }
19556         
19557         this.store.each(function(v) { 
19558             if (cselitem) {
19559                 // start at existing selection.
19560                 if (cselitem.id == v.id) {
19561                     cselitem = false;
19562                 }
19563                 return;
19564             }
19565                 
19566             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19567                 match = this.store.indexOf(v);
19568                 return false;
19569             }
19570         }, this);
19571         
19572         if (match === false) {
19573             return true; // no more action?
19574         }
19575         // scroll to?
19576         this.view.select(match);
19577         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19578         sn.scrollIntoView(sn.dom.parentNode, false);
19579     }
19580
19581     /** 
19582     * @cfg {Boolean} grow 
19583     * @hide 
19584     */
19585     /** 
19586     * @cfg {Number} growMin 
19587     * @hide 
19588     */
19589     /** 
19590     * @cfg {Number} growMax 
19591     * @hide 
19592     */
19593     /**
19594      * @hide
19595      * @method autoSize
19596      */
19597 });/*
19598  * Copyright(c) 2010-2012, Roo J Solutions Limited
19599  *
19600  * Licence LGPL
19601  *
19602  */
19603
19604 /**
19605  * @class Roo.form.ComboBoxArray
19606  * @extends Roo.form.TextField
19607  * A facebook style adder... for lists of email / people / countries  etc...
19608  * pick multiple items from a combo box, and shows each one.
19609  *
19610  *  Fred [x]  Brian [x]  [Pick another |v]
19611  *
19612  *
19613  *  For this to work: it needs various extra information
19614  *    - normal combo problay has
19615  *      name, hiddenName
19616  *    + displayField, valueField
19617  *
19618  *    For our purpose...
19619  *
19620  *
19621  *   If we change from 'extends' to wrapping...
19622  *   
19623  *  
19624  *
19625  
19626  
19627  * @constructor
19628  * Create a new ComboBoxArray.
19629  * @param {Object} config Configuration options
19630  */
19631  
19632
19633 Roo.form.ComboBoxArray = function(config)
19634 {
19635     this.addEvents({
19636         /**
19637          * @event beforeremove
19638          * Fires before remove the value from the list
19639              * @param {Roo.form.ComboBoxArray} _self This combo box array
19640              * @param {Roo.form.ComboBoxArray.Item} item removed item
19641              */
19642         'beforeremove' : true,
19643         /**
19644          * @event remove
19645          * Fires when remove the value from the list
19646              * @param {Roo.form.ComboBoxArray} _self This combo box array
19647              * @param {Roo.form.ComboBoxArray.Item} item removed item
19648              */
19649         'remove' : true
19650         
19651         
19652     });
19653     
19654     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19655     
19656     this.items = new Roo.util.MixedCollection(false);
19657     
19658     // construct the child combo...
19659     
19660     
19661     
19662     
19663    
19664     
19665 }
19666
19667  
19668 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19669
19670     /**
19671      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19672      */
19673     
19674     lastData : false,
19675     
19676     // behavies liek a hiddne field
19677     inputType:      'hidden',
19678     /**
19679      * @cfg {Number} width The width of the box that displays the selected element
19680      */ 
19681     width:          300,
19682
19683     
19684     
19685     /**
19686      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19687      */
19688     name : false,
19689     /**
19690      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19691      */
19692     hiddenName : false,
19693     
19694     
19695     // private the array of items that are displayed..
19696     items  : false,
19697     // private - the hidden field el.
19698     hiddenEl : false,
19699     // private - the filed el..
19700     el : false,
19701     
19702     //validateValue : function() { return true; }, // all values are ok!
19703     //onAddClick: function() { },
19704     
19705     onRender : function(ct, position) 
19706     {
19707         
19708         // create the standard hidden element
19709         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19710         
19711         
19712         // give fake names to child combo;
19713         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19714         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19715         
19716         this.combo = Roo.factory(this.combo, Roo.form);
19717         this.combo.onRender(ct, position);
19718         if (typeof(this.combo.width) != 'undefined') {
19719             this.combo.onResize(this.combo.width,0);
19720         }
19721         
19722         this.combo.initEvents();
19723         
19724         // assigned so form know we need to do this..
19725         this.store          = this.combo.store;
19726         this.valueField     = this.combo.valueField;
19727         this.displayField   = this.combo.displayField ;
19728         
19729         
19730         this.combo.wrap.addClass('x-cbarray-grp');
19731         
19732         var cbwrap = this.combo.wrap.createChild(
19733             {tag: 'div', cls: 'x-cbarray-cb'},
19734             this.combo.el.dom
19735         );
19736         
19737              
19738         this.hiddenEl = this.combo.wrap.createChild({
19739             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19740         });
19741         this.el = this.combo.wrap.createChild({
19742             tag: 'input',  type:'hidden' , name: this.name, value : ''
19743         });
19744          //   this.el.dom.removeAttribute("name");
19745         
19746         
19747         this.outerWrap = this.combo.wrap;
19748         this.wrap = cbwrap;
19749         
19750         this.outerWrap.setWidth(this.width);
19751         this.outerWrap.dom.removeChild(this.el.dom);
19752         
19753         this.wrap.dom.appendChild(this.el.dom);
19754         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19755         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19756         
19757         this.combo.trigger.setStyle('position','relative');
19758         this.combo.trigger.setStyle('left', '0px');
19759         this.combo.trigger.setStyle('top', '2px');
19760         
19761         this.combo.el.setStyle('vertical-align', 'text-bottom');
19762         
19763         //this.trigger.setStyle('vertical-align', 'top');
19764         
19765         // this should use the code from combo really... on('add' ....)
19766         if (this.adder) {
19767             
19768         
19769             this.adder = this.outerWrap.createChild(
19770                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19771             var _t = this;
19772             this.adder.on('click', function(e) {
19773                 _t.fireEvent('adderclick', this, e);
19774             }, _t);
19775         }
19776         //var _t = this;
19777         //this.adder.on('click', this.onAddClick, _t);
19778         
19779         
19780         this.combo.on('select', function(cb, rec, ix) {
19781             this.addItem(rec.data);
19782             
19783             cb.setValue('');
19784             cb.el.dom.value = '';
19785             //cb.lastData = rec.data;
19786             // add to list
19787             
19788         }, this);
19789         
19790         
19791     },
19792     
19793     
19794     getName: function()
19795     {
19796         // returns hidden if it's set..
19797         if (!this.rendered) {return ''};
19798         return  this.hiddenName ? this.hiddenName : this.name;
19799         
19800     },
19801     
19802     
19803     onResize: function(w, h){
19804         
19805         return;
19806         // not sure if this is needed..
19807         //this.combo.onResize(w,h);
19808         
19809         if(typeof w != 'number'){
19810             // we do not handle it!?!?
19811             return;
19812         }
19813         var tw = this.combo.trigger.getWidth();
19814         tw += this.addicon ? this.addicon.getWidth() : 0;
19815         tw += this.editicon ? this.editicon.getWidth() : 0;
19816         var x = w - tw;
19817         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19818             
19819         this.combo.trigger.setStyle('left', '0px');
19820         
19821         if(this.list && this.listWidth === undefined){
19822             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19823             this.list.setWidth(lw);
19824             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19825         }
19826         
19827     
19828         
19829     },
19830     
19831     addItem: function(rec)
19832     {
19833         var valueField = this.combo.valueField;
19834         var displayField = this.combo.displayField;
19835         if (this.items.indexOfKey(rec[valueField]) > -1) {
19836             //console.log("GOT " + rec.data.id);
19837             return;
19838         }
19839         
19840         var x = new Roo.form.ComboBoxArray.Item({
19841             //id : rec[this.idField],
19842             data : rec,
19843             displayField : displayField ,
19844             tipField : displayField ,
19845             cb : this
19846         });
19847         // use the 
19848         this.items.add(rec[valueField],x);
19849         // add it before the element..
19850         this.updateHiddenEl();
19851         x.render(this.outerWrap, this.wrap.dom);
19852         // add the image handler..
19853     },
19854     
19855     updateHiddenEl : function()
19856     {
19857         this.validate();
19858         if (!this.hiddenEl) {
19859             return;
19860         }
19861         var ar = [];
19862         var idField = this.combo.valueField;
19863         
19864         this.items.each(function(f) {
19865             ar.push(f.data[idField]);
19866            
19867         });
19868         this.hiddenEl.dom.value = ar.join(',');
19869         this.validate();
19870     },
19871     
19872     reset : function()
19873     {
19874         this.items.clear();
19875         
19876         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19877            el.remove();
19878         });
19879         
19880         this.el.dom.value = '';
19881         if (this.hiddenEl) {
19882             this.hiddenEl.dom.value = '';
19883         }
19884         
19885     },
19886     getValue: function()
19887     {
19888         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19889     },
19890     setValue: function(v) // not a valid action - must use addItems..
19891     {
19892          
19893         this.reset();
19894         
19895         
19896         
19897         if (this.store.isLocal && (typeof(v) == 'string')) {
19898             // then we can use the store to find the values..
19899             // comma seperated at present.. this needs to allow JSON based encoding..
19900             this.hiddenEl.value  = v;
19901             var v_ar = [];
19902             Roo.each(v.split(','), function(k) {
19903                 Roo.log("CHECK " + this.valueField + ',' + k);
19904                 var li = this.store.query(this.valueField, k);
19905                 if (!li.length) {
19906                     return;
19907                 }
19908                 var add = {};
19909                 add[this.valueField] = k;
19910                 add[this.displayField] = li.item(0).data[this.displayField];
19911                 
19912                 this.addItem(add);
19913             }, this) 
19914              
19915         }
19916         if (typeof(v) == 'object' ) {
19917             // then let's assume it's an array of objects..
19918             Roo.each(v, function(l) {
19919                 this.addItem(l);
19920             }, this);
19921              
19922         }
19923         
19924         
19925     },
19926     setFromData: function(v)
19927     {
19928         // this recieves an object, if setValues is called.
19929         this.reset();
19930         this.el.dom.value = v[this.displayField];
19931         this.hiddenEl.dom.value = v[this.valueField];
19932         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19933             return;
19934         }
19935         var kv = v[this.valueField];
19936         var dv = v[this.displayField];
19937         kv = typeof(kv) != 'string' ? '' : kv;
19938         dv = typeof(dv) != 'string' ? '' : dv;
19939         
19940         
19941         var keys = kv.split(',');
19942         var display = dv.split(',');
19943         for (var i = 0 ; i < keys.length; i++) {
19944             
19945             add = {};
19946             add[this.valueField] = keys[i];
19947             add[this.displayField] = display[i];
19948             this.addItem(add);
19949         }
19950       
19951         
19952     },
19953     
19954     /**
19955      * Validates the combox array value
19956      * @return {Boolean} True if the value is valid, else false
19957      */
19958     validate : function(){
19959         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19960             this.clearInvalid();
19961             return true;
19962         }
19963         return false;
19964     },
19965     
19966     validateValue : function(value){
19967         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19968         
19969     },
19970     
19971     /*@
19972      * overide
19973      * 
19974      */
19975     isDirty : function() {
19976         if(this.disabled) {
19977             return false;
19978         }
19979         
19980         try {
19981             var d = Roo.decode(String(this.originalValue));
19982         } catch (e) {
19983             return String(this.getValue()) !== String(this.originalValue);
19984         }
19985         
19986         var originalValue = [];
19987         
19988         for (var i = 0; i < d.length; i++){
19989             originalValue.push(d[i][this.valueField]);
19990         }
19991         
19992         return String(this.getValue()) !== String(originalValue.join(','));
19993         
19994     }
19995     
19996 });
19997
19998
19999
20000 /**
20001  * @class Roo.form.ComboBoxArray.Item
20002  * @extends Roo.BoxComponent
20003  * A selected item in the list
20004  *  Fred [x]  Brian [x]  [Pick another |v]
20005  * 
20006  * @constructor
20007  * Create a new item.
20008  * @param {Object} config Configuration options
20009  */
20010  
20011 Roo.form.ComboBoxArray.Item = function(config) {
20012     config.id = Roo.id();
20013     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20014 }
20015
20016 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20017     data : {},
20018     cb: false,
20019     displayField : false,
20020     tipField : false,
20021     
20022     
20023     defaultAutoCreate : {
20024         tag: 'div',
20025         cls: 'x-cbarray-item',
20026         cn : [ 
20027             { tag: 'div' },
20028             {
20029                 tag: 'img',
20030                 width:16,
20031                 height : 16,
20032                 src : Roo.BLANK_IMAGE_URL ,
20033                 align: 'center'
20034             }
20035         ]
20036         
20037     },
20038     
20039  
20040     onRender : function(ct, position)
20041     {
20042         Roo.form.Field.superclass.onRender.call(this, ct, position);
20043         
20044         if(!this.el){
20045             var cfg = this.getAutoCreate();
20046             this.el = ct.createChild(cfg, position);
20047         }
20048         
20049         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20050         
20051         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20052             this.cb.renderer(this.data) :
20053             String.format('{0}',this.data[this.displayField]);
20054         
20055             
20056         this.el.child('div').dom.setAttribute('qtip',
20057                         String.format('{0}',this.data[this.tipField])
20058         );
20059         
20060         this.el.child('img').on('click', this.remove, this);
20061         
20062     },
20063    
20064     remove : function()
20065     {
20066         if(this.cb.disabled){
20067             return;
20068         }
20069         
20070         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20071             this.cb.items.remove(this);
20072             this.el.child('img').un('click', this.remove, this);
20073             this.el.remove();
20074             this.cb.updateHiddenEl();
20075
20076             this.cb.fireEvent('remove', this.cb, this);
20077         }
20078         
20079     }
20080 });/*
20081  * Based on:
20082  * Ext JS Library 1.1.1
20083  * Copyright(c) 2006-2007, Ext JS, LLC.
20084  *
20085  * Originally Released Under LGPL - original licence link has changed is not relivant.
20086  *
20087  * Fork - LGPL
20088  * <script type="text/javascript">
20089  */
20090 /**
20091  * @class Roo.form.Checkbox
20092  * @extends Roo.form.Field
20093  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20094  * @constructor
20095  * Creates a new Checkbox
20096  * @param {Object} config Configuration options
20097  */
20098 Roo.form.Checkbox = function(config){
20099     Roo.form.Checkbox.superclass.constructor.call(this, config);
20100     this.addEvents({
20101         /**
20102          * @event check
20103          * Fires when the checkbox is checked or unchecked.
20104              * @param {Roo.form.Checkbox} this This checkbox
20105              * @param {Boolean} checked The new checked value
20106              */
20107         check : true
20108     });
20109 };
20110
20111 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20112     /**
20113      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20114      */
20115     focusClass : undefined,
20116     /**
20117      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20118      */
20119     fieldClass: "x-form-field",
20120     /**
20121      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20122      */
20123     checked: false,
20124     /**
20125      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20126      * {tag: "input", type: "checkbox", autocomplete: "off"})
20127      */
20128     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20129     /**
20130      * @cfg {String} boxLabel The text that appears beside the checkbox
20131      */
20132     boxLabel : "",
20133     /**
20134      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20135      */  
20136     inputValue : '1',
20137     /**
20138      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20139      */
20140      valueOff: '0', // value when not checked..
20141
20142     actionMode : 'viewEl', 
20143     //
20144     // private
20145     itemCls : 'x-menu-check-item x-form-item',
20146     groupClass : 'x-menu-group-item',
20147     inputType : 'hidden',
20148     
20149     
20150     inSetChecked: false, // check that we are not calling self...
20151     
20152     inputElement: false, // real input element?
20153     basedOn: false, // ????
20154     
20155     isFormField: true, // not sure where this is needed!!!!
20156
20157     onResize : function(){
20158         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20159         if(!this.boxLabel){
20160             this.el.alignTo(this.wrap, 'c-c');
20161         }
20162     },
20163
20164     initEvents : function(){
20165         Roo.form.Checkbox.superclass.initEvents.call(this);
20166         this.el.on("click", this.onClick,  this);
20167         this.el.on("change", this.onClick,  this);
20168     },
20169
20170
20171     getResizeEl : function(){
20172         return this.wrap;
20173     },
20174
20175     getPositionEl : function(){
20176         return this.wrap;
20177     },
20178
20179     // private
20180     onRender : function(ct, position){
20181         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20182         /*
20183         if(this.inputValue !== undefined){
20184             this.el.dom.value = this.inputValue;
20185         }
20186         */
20187         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20188         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20189         var viewEl = this.wrap.createChild({ 
20190             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20191         this.viewEl = viewEl;   
20192         this.wrap.on('click', this.onClick,  this); 
20193         
20194         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20195         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20196         
20197         
20198         
20199         if(this.boxLabel){
20200             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20201         //    viewEl.on('click', this.onClick,  this); 
20202         }
20203         //if(this.checked){
20204             this.setChecked(this.checked);
20205         //}else{
20206             //this.checked = this.el.dom;
20207         //}
20208
20209     },
20210
20211     // private
20212     initValue : Roo.emptyFn,
20213
20214     /**
20215      * Returns the checked state of the checkbox.
20216      * @return {Boolean} True if checked, else false
20217      */
20218     getValue : function(){
20219         if(this.el){
20220             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20221         }
20222         return this.valueOff;
20223         
20224     },
20225
20226         // private
20227     onClick : function(){ 
20228         if (this.disabled) {
20229             return;
20230         }
20231         this.setChecked(!this.checked);
20232
20233         //if(this.el.dom.checked != this.checked){
20234         //    this.setValue(this.el.dom.checked);
20235        // }
20236     },
20237
20238     /**
20239      * Sets the checked state of the checkbox.
20240      * On is always based on a string comparison between inputValue and the param.
20241      * @param {Boolean/String} value - the value to set 
20242      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20243      */
20244     setValue : function(v,suppressEvent){
20245         
20246         
20247         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20248         //if(this.el && this.el.dom){
20249         //    this.el.dom.checked = this.checked;
20250         //    this.el.dom.defaultChecked = this.checked;
20251         //}
20252         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20253         //this.fireEvent("check", this, this.checked);
20254     },
20255     // private..
20256     setChecked : function(state,suppressEvent)
20257     {
20258         if (this.inSetChecked) {
20259             this.checked = state;
20260             return;
20261         }
20262         
20263     
20264         if(this.wrap){
20265             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20266         }
20267         this.checked = state;
20268         if(suppressEvent !== true){
20269             this.fireEvent('check', this, state);
20270         }
20271         this.inSetChecked = true;
20272         this.el.dom.value = state ? this.inputValue : this.valueOff;
20273         this.inSetChecked = false;
20274         
20275     },
20276     // handle setting of hidden value by some other method!!?!?
20277     setFromHidden: function()
20278     {
20279         if(!this.el){
20280             return;
20281         }
20282         //console.log("SET FROM HIDDEN");
20283         //alert('setFrom hidden');
20284         this.setValue(this.el.dom.value);
20285     },
20286     
20287     onDestroy : function()
20288     {
20289         if(this.viewEl){
20290             Roo.get(this.viewEl).remove();
20291         }
20292          
20293         Roo.form.Checkbox.superclass.onDestroy.call(this);
20294     },
20295     
20296     setBoxLabel : function(str)
20297     {
20298         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20299     }
20300
20301 });/*
20302  * Based on:
20303  * Ext JS Library 1.1.1
20304  * Copyright(c) 2006-2007, Ext JS, LLC.
20305  *
20306  * Originally Released Under LGPL - original licence link has changed is not relivant.
20307  *
20308  * Fork - LGPL
20309  * <script type="text/javascript">
20310  */
20311  
20312 /**
20313  * @class Roo.form.Radio
20314  * @extends Roo.form.Checkbox
20315  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20316  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20317  * @constructor
20318  * Creates a new Radio
20319  * @param {Object} config Configuration options
20320  */
20321 Roo.form.Radio = function(){
20322     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20323 };
20324 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20325     inputType: 'radio',
20326
20327     /**
20328      * If this radio is part of a group, it will return the selected value
20329      * @return {String}
20330      */
20331     getGroupValue : function(){
20332         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20333     },
20334     
20335     
20336     onRender : function(ct, position){
20337         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20338         
20339         if(this.inputValue !== undefined){
20340             this.el.dom.value = this.inputValue;
20341         }
20342          
20343         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20344         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20345         //var viewEl = this.wrap.createChild({ 
20346         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20347         //this.viewEl = viewEl;   
20348         //this.wrap.on('click', this.onClick,  this); 
20349         
20350         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20351         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20352         
20353         
20354         
20355         if(this.boxLabel){
20356             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20357         //    viewEl.on('click', this.onClick,  this); 
20358         }
20359          if(this.checked){
20360             this.el.dom.checked =   'checked' ;
20361         }
20362          
20363     } 
20364     
20365     
20366 });//<script type="text/javascript">
20367
20368 /*
20369  * Based  Ext JS Library 1.1.1
20370  * Copyright(c) 2006-2007, Ext JS, LLC.
20371  * LGPL
20372  *
20373  */
20374  
20375 /**
20376  * @class Roo.HtmlEditorCore
20377  * @extends Roo.Component
20378  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20379  *
20380  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20381  */
20382
20383 Roo.HtmlEditorCore = function(config){
20384     
20385     
20386     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20387     
20388     
20389     this.addEvents({
20390         /**
20391          * @event initialize
20392          * Fires when the editor is fully initialized (including the iframe)
20393          * @param {Roo.HtmlEditorCore} this
20394          */
20395         initialize: true,
20396         /**
20397          * @event activate
20398          * Fires when the editor is first receives the focus. Any insertion must wait
20399          * until after this event.
20400          * @param {Roo.HtmlEditorCore} this
20401          */
20402         activate: true,
20403          /**
20404          * @event beforesync
20405          * Fires before the textarea is updated with content from the editor iframe. Return false
20406          * to cancel the sync.
20407          * @param {Roo.HtmlEditorCore} this
20408          * @param {String} html
20409          */
20410         beforesync: true,
20411          /**
20412          * @event beforepush
20413          * Fires before the iframe editor is updated with content from the textarea. Return false
20414          * to cancel the push.
20415          * @param {Roo.HtmlEditorCore} this
20416          * @param {String} html
20417          */
20418         beforepush: true,
20419          /**
20420          * @event sync
20421          * Fires when the textarea is updated with content from the editor iframe.
20422          * @param {Roo.HtmlEditorCore} this
20423          * @param {String} html
20424          */
20425         sync: true,
20426          /**
20427          * @event push
20428          * Fires when the iframe editor is updated with content from the textarea.
20429          * @param {Roo.HtmlEditorCore} this
20430          * @param {String} html
20431          */
20432         push: true,
20433         
20434         /**
20435          * @event editorevent
20436          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20437          * @param {Roo.HtmlEditorCore} this
20438          */
20439         editorevent: true
20440         
20441     });
20442     
20443     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20444     
20445     // defaults : white / black...
20446     this.applyBlacklists();
20447     
20448     
20449     
20450 };
20451
20452
20453 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20454
20455
20456      /**
20457      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20458      */
20459     
20460     owner : false,
20461     
20462      /**
20463      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20464      *                        Roo.resizable.
20465      */
20466     resizable : false,
20467      /**
20468      * @cfg {Number} height (in pixels)
20469      */   
20470     height: 300,
20471    /**
20472      * @cfg {Number} width (in pixels)
20473      */   
20474     width: 500,
20475     
20476     /**
20477      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20478      * 
20479      */
20480     stylesheets: false,
20481     
20482     // id of frame..
20483     frameId: false,
20484     
20485     // private properties
20486     validationEvent : false,
20487     deferHeight: true,
20488     initialized : false,
20489     activated : false,
20490     sourceEditMode : false,
20491     onFocus : Roo.emptyFn,
20492     iframePad:3,
20493     hideMode:'offsets',
20494     
20495     clearUp: true,
20496     
20497     // blacklist + whitelisted elements..
20498     black: false,
20499     white: false,
20500      
20501     bodyCls : '',
20502
20503     /**
20504      * Protected method that will not generally be called directly. It
20505      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20506      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20507      */
20508     getDocMarkup : function(){
20509         // body styles..
20510         var st = '';
20511         
20512         // inherit styels from page...?? 
20513         if (this.stylesheets === false) {
20514             
20515             Roo.get(document.head).select('style').each(function(node) {
20516                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20517             });
20518             
20519             Roo.get(document.head).select('link').each(function(node) { 
20520                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20521             });
20522             
20523         } else if (!this.stylesheets.length) {
20524                 // simple..
20525                 st = '<style type="text/css">' +
20526                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20527                    '</style>';
20528         } else { 
20529             st = '<style type="text/css">' +
20530                     this.stylesheets +
20531                 '</style>';
20532         }
20533         
20534         st +=  '<style type="text/css">' +
20535             'IMG { cursor: pointer } ' +
20536         '</style>';
20537
20538         var cls = 'roo-htmleditor-body';
20539         
20540         if(this.bodyCls.length){
20541             cls += ' ' + this.bodyCls;
20542         }
20543         
20544         return '<html><head>' + st  +
20545             //<style type="text/css">' +
20546             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20547             //'</style>' +
20548             ' </head><body class="' +  cls + '"></body></html>';
20549     },
20550
20551     // private
20552     onRender : function(ct, position)
20553     {
20554         var _t = this;
20555         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20556         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20557         
20558         
20559         this.el.dom.style.border = '0 none';
20560         this.el.dom.setAttribute('tabIndex', -1);
20561         this.el.addClass('x-hidden hide');
20562         
20563         
20564         
20565         if(Roo.isIE){ // fix IE 1px bogus margin
20566             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20567         }
20568        
20569         
20570         this.frameId = Roo.id();
20571         
20572          
20573         
20574         var iframe = this.owner.wrap.createChild({
20575             tag: 'iframe',
20576             cls: 'form-control', // bootstrap..
20577             id: this.frameId,
20578             name: this.frameId,
20579             frameBorder : 'no',
20580             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20581         }, this.el
20582         );
20583         
20584         
20585         this.iframe = iframe.dom;
20586
20587          this.assignDocWin();
20588         
20589         this.doc.designMode = 'on';
20590        
20591         this.doc.open();
20592         this.doc.write(this.getDocMarkup());
20593         this.doc.close();
20594
20595         
20596         var task = { // must defer to wait for browser to be ready
20597             run : function(){
20598                 //console.log("run task?" + this.doc.readyState);
20599                 this.assignDocWin();
20600                 if(this.doc.body || this.doc.readyState == 'complete'){
20601                     try {
20602                         this.doc.designMode="on";
20603                     } catch (e) {
20604                         return;
20605                     }
20606                     Roo.TaskMgr.stop(task);
20607                     this.initEditor.defer(10, this);
20608                 }
20609             },
20610             interval : 10,
20611             duration: 10000,
20612             scope: this
20613         };
20614         Roo.TaskMgr.start(task);
20615
20616     },
20617
20618     // private
20619     onResize : function(w, h)
20620     {
20621          Roo.log('resize: ' +w + ',' + h );
20622         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20623         if(!this.iframe){
20624             return;
20625         }
20626         if(typeof w == 'number'){
20627             
20628             this.iframe.style.width = w + 'px';
20629         }
20630         if(typeof h == 'number'){
20631             
20632             this.iframe.style.height = h + 'px';
20633             if(this.doc){
20634                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20635             }
20636         }
20637         
20638     },
20639
20640     /**
20641      * Toggles the editor between standard and source edit mode.
20642      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20643      */
20644     toggleSourceEdit : function(sourceEditMode){
20645         
20646         this.sourceEditMode = sourceEditMode === true;
20647         
20648         if(this.sourceEditMode){
20649  
20650             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20651             
20652         }else{
20653             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20654             //this.iframe.className = '';
20655             this.deferFocus();
20656         }
20657         //this.setSize(this.owner.wrap.getSize());
20658         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20659     },
20660
20661     
20662   
20663
20664     /**
20665      * Protected method that will not generally be called directly. If you need/want
20666      * custom HTML cleanup, this is the method you should override.
20667      * @param {String} html The HTML to be cleaned
20668      * return {String} The cleaned HTML
20669      */
20670     cleanHtml : function(html){
20671         html = String(html);
20672         if(html.length > 5){
20673             if(Roo.isSafari){ // strip safari nonsense
20674                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20675             }
20676         }
20677         if(html == '&nbsp;'){
20678             html = '';
20679         }
20680         return html;
20681     },
20682
20683     /**
20684      * HTML Editor -> Textarea
20685      * Protected method that will not generally be called directly. Syncs the contents
20686      * of the editor iframe with the textarea.
20687      */
20688     syncValue : function(){
20689         if(this.initialized){
20690             var bd = (this.doc.body || this.doc.documentElement);
20691             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20692             var html = bd.innerHTML;
20693             if(Roo.isSafari){
20694                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20695                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20696                 if(m && m[1]){
20697                     html = '<div style="'+m[0]+'">' + html + '</div>';
20698                 }
20699             }
20700             html = this.cleanHtml(html);
20701             // fix up the special chars.. normaly like back quotes in word...
20702             // however we do not want to do this with chinese..
20703             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20704                 var cc = b.charCodeAt();
20705                 if (
20706                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20707                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20708                     (cc >= 0xf900 && cc < 0xfb00 )
20709                 ) {
20710                         return b;
20711                 }
20712                 return "&#"+cc+";" 
20713             });
20714             if(this.owner.fireEvent('beforesync', this, html) !== false){
20715                 this.el.dom.value = html;
20716                 this.owner.fireEvent('sync', this, html);
20717             }
20718         }
20719     },
20720
20721     /**
20722      * Protected method that will not generally be called directly. Pushes the value of the textarea
20723      * into the iframe editor.
20724      */
20725     pushValue : function(){
20726         if(this.initialized){
20727             var v = this.el.dom.value.trim();
20728             
20729 //            if(v.length < 1){
20730 //                v = '&#160;';
20731 //            }
20732             
20733             if(this.owner.fireEvent('beforepush', this, v) !== false){
20734                 var d = (this.doc.body || this.doc.documentElement);
20735                 d.innerHTML = v;
20736                 this.cleanUpPaste();
20737                 this.el.dom.value = d.innerHTML;
20738                 this.owner.fireEvent('push', this, v);
20739             }
20740         }
20741     },
20742
20743     // private
20744     deferFocus : function(){
20745         this.focus.defer(10, this);
20746     },
20747
20748     // doc'ed in Field
20749     focus : function(){
20750         if(this.win && !this.sourceEditMode){
20751             this.win.focus();
20752         }else{
20753             this.el.focus();
20754         }
20755     },
20756     
20757     assignDocWin: function()
20758     {
20759         var iframe = this.iframe;
20760         
20761          if(Roo.isIE){
20762             this.doc = iframe.contentWindow.document;
20763             this.win = iframe.contentWindow;
20764         } else {
20765 //            if (!Roo.get(this.frameId)) {
20766 //                return;
20767 //            }
20768 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20769 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20770             
20771             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20772                 return;
20773             }
20774             
20775             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20776             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20777         }
20778     },
20779     
20780     // private
20781     initEditor : function(){
20782         //console.log("INIT EDITOR");
20783         this.assignDocWin();
20784         
20785         
20786         
20787         this.doc.designMode="on";
20788         this.doc.open();
20789         this.doc.write(this.getDocMarkup());
20790         this.doc.close();
20791         
20792         var dbody = (this.doc.body || this.doc.documentElement);
20793         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20794         // this copies styles from the containing element into thsi one..
20795         // not sure why we need all of this..
20796         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20797         
20798         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20799         //ss['background-attachment'] = 'fixed'; // w3c
20800         dbody.bgProperties = 'fixed'; // ie
20801         //Roo.DomHelper.applyStyles(dbody, ss);
20802         Roo.EventManager.on(this.doc, {
20803             //'mousedown': this.onEditorEvent,
20804             'mouseup': this.onEditorEvent,
20805             'dblclick': this.onEditorEvent,
20806             'click': this.onEditorEvent,
20807             'keyup': this.onEditorEvent,
20808             buffer:100,
20809             scope: this
20810         });
20811         if(Roo.isGecko){
20812             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20813         }
20814         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20815             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20816         }
20817         this.initialized = true;
20818
20819         this.owner.fireEvent('initialize', this);
20820         this.pushValue();
20821     },
20822
20823     // private
20824     onDestroy : function(){
20825         
20826         
20827         
20828         if(this.rendered){
20829             
20830             //for (var i =0; i < this.toolbars.length;i++) {
20831             //    // fixme - ask toolbars for heights?
20832             //    this.toolbars[i].onDestroy();
20833            // }
20834             
20835             //this.wrap.dom.innerHTML = '';
20836             //this.wrap.remove();
20837         }
20838     },
20839
20840     // private
20841     onFirstFocus : function(){
20842         
20843         this.assignDocWin();
20844         
20845         
20846         this.activated = true;
20847          
20848     
20849         if(Roo.isGecko){ // prevent silly gecko errors
20850             this.win.focus();
20851             var s = this.win.getSelection();
20852             if(!s.focusNode || s.focusNode.nodeType != 3){
20853                 var r = s.getRangeAt(0);
20854                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20855                 r.collapse(true);
20856                 this.deferFocus();
20857             }
20858             try{
20859                 this.execCmd('useCSS', true);
20860                 this.execCmd('styleWithCSS', false);
20861             }catch(e){}
20862         }
20863         this.owner.fireEvent('activate', this);
20864     },
20865
20866     // private
20867     adjustFont: function(btn){
20868         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20869         //if(Roo.isSafari){ // safari
20870         //    adjust *= 2;
20871        // }
20872         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20873         if(Roo.isSafari){ // safari
20874             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20875             v =  (v < 10) ? 10 : v;
20876             v =  (v > 48) ? 48 : v;
20877             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20878             
20879         }
20880         
20881         
20882         v = Math.max(1, v+adjust);
20883         
20884         this.execCmd('FontSize', v  );
20885     },
20886
20887     onEditorEvent : function(e)
20888     {
20889         this.owner.fireEvent('editorevent', this, e);
20890       //  this.updateToolbar();
20891         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20892     },
20893
20894     insertTag : function(tg)
20895     {
20896         // could be a bit smarter... -> wrap the current selected tRoo..
20897         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20898             
20899             range = this.createRange(this.getSelection());
20900             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20901             wrappingNode.appendChild(range.extractContents());
20902             range.insertNode(wrappingNode);
20903
20904             return;
20905             
20906             
20907             
20908         }
20909         this.execCmd("formatblock",   tg);
20910         
20911     },
20912     
20913     insertText : function(txt)
20914     {
20915         
20916         
20917         var range = this.createRange();
20918         range.deleteContents();
20919                //alert(Sender.getAttribute('label'));
20920                
20921         range.insertNode(this.doc.createTextNode(txt));
20922     } ,
20923     
20924      
20925
20926     /**
20927      * Executes a Midas editor command on the editor document and performs necessary focus and
20928      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20929      * @param {String} cmd The Midas command
20930      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20931      */
20932     relayCmd : function(cmd, value){
20933         this.win.focus();
20934         this.execCmd(cmd, value);
20935         this.owner.fireEvent('editorevent', this);
20936         //this.updateToolbar();
20937         this.owner.deferFocus();
20938     },
20939
20940     /**
20941      * Executes a Midas editor command directly on the editor document.
20942      * For visual commands, you should use {@link #relayCmd} instead.
20943      * <b>This should only be called after the editor is initialized.</b>
20944      * @param {String} cmd The Midas command
20945      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20946      */
20947     execCmd : function(cmd, value){
20948         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20949         this.syncValue();
20950     },
20951  
20952  
20953    
20954     /**
20955      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20956      * to insert tRoo.
20957      * @param {String} text | dom node.. 
20958      */
20959     insertAtCursor : function(text)
20960     {
20961         
20962         if(!this.activated){
20963             return;
20964         }
20965         /*
20966         if(Roo.isIE){
20967             this.win.focus();
20968             var r = this.doc.selection.createRange();
20969             if(r){
20970                 r.collapse(true);
20971                 r.pasteHTML(text);
20972                 this.syncValue();
20973                 this.deferFocus();
20974             
20975             }
20976             return;
20977         }
20978         */
20979         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20980             this.win.focus();
20981             
20982             
20983             // from jquery ui (MIT licenced)
20984             var range, node;
20985             var win = this.win;
20986             
20987             if (win.getSelection && win.getSelection().getRangeAt) {
20988                 range = win.getSelection().getRangeAt(0);
20989                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20990                 range.insertNode(node);
20991             } else if (win.document.selection && win.document.selection.createRange) {
20992                 // no firefox support
20993                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20994                 win.document.selection.createRange().pasteHTML(txt);
20995             } else {
20996                 // no firefox support
20997                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20998                 this.execCmd('InsertHTML', txt);
20999             } 
21000             
21001             this.syncValue();
21002             
21003             this.deferFocus();
21004         }
21005     },
21006  // private
21007     mozKeyPress : function(e){
21008         if(e.ctrlKey){
21009             var c = e.getCharCode(), cmd;
21010           
21011             if(c > 0){
21012                 c = String.fromCharCode(c).toLowerCase();
21013                 switch(c){
21014                     case 'b':
21015                         cmd = 'bold';
21016                         break;
21017                     case 'i':
21018                         cmd = 'italic';
21019                         break;
21020                     
21021                     case 'u':
21022                         cmd = 'underline';
21023                         break;
21024                     
21025                     case 'v':
21026                         this.cleanUpPaste.defer(100, this);
21027                         return;
21028                         
21029                 }
21030                 if(cmd){
21031                     this.win.focus();
21032                     this.execCmd(cmd);
21033                     this.deferFocus();
21034                     e.preventDefault();
21035                 }
21036                 
21037             }
21038         }
21039     },
21040
21041     // private
21042     fixKeys : function(){ // load time branching for fastest keydown performance
21043         if(Roo.isIE){
21044             return function(e){
21045                 var k = e.getKey(), r;
21046                 if(k == e.TAB){
21047                     e.stopEvent();
21048                     r = this.doc.selection.createRange();
21049                     if(r){
21050                         r.collapse(true);
21051                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21052                         this.deferFocus();
21053                     }
21054                     return;
21055                 }
21056                 
21057                 if(k == e.ENTER){
21058                     r = this.doc.selection.createRange();
21059                     if(r){
21060                         var target = r.parentElement();
21061                         if(!target || target.tagName.toLowerCase() != 'li'){
21062                             e.stopEvent();
21063                             r.pasteHTML('<br />');
21064                             r.collapse(false);
21065                             r.select();
21066                         }
21067                     }
21068                 }
21069                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21070                     this.cleanUpPaste.defer(100, this);
21071                     return;
21072                 }
21073                 
21074                 
21075             };
21076         }else if(Roo.isOpera){
21077             return function(e){
21078                 var k = e.getKey();
21079                 if(k == e.TAB){
21080                     e.stopEvent();
21081                     this.win.focus();
21082                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21083                     this.deferFocus();
21084                 }
21085                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21086                     this.cleanUpPaste.defer(100, this);
21087                     return;
21088                 }
21089                 
21090             };
21091         }else if(Roo.isSafari){
21092             return function(e){
21093                 var k = e.getKey();
21094                 
21095                 if(k == e.TAB){
21096                     e.stopEvent();
21097                     this.execCmd('InsertText','\t');
21098                     this.deferFocus();
21099                     return;
21100                 }
21101                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21102                     this.cleanUpPaste.defer(100, this);
21103                     return;
21104                 }
21105                 
21106              };
21107         }
21108     }(),
21109     
21110     getAllAncestors: function()
21111     {
21112         var p = this.getSelectedNode();
21113         var a = [];
21114         if (!p) {
21115             a.push(p); // push blank onto stack..
21116             p = this.getParentElement();
21117         }
21118         
21119         
21120         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21121             a.push(p);
21122             p = p.parentNode;
21123         }
21124         a.push(this.doc.body);
21125         return a;
21126     },
21127     lastSel : false,
21128     lastSelNode : false,
21129     
21130     
21131     getSelection : function() 
21132     {
21133         this.assignDocWin();
21134         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21135     },
21136     
21137     getSelectedNode: function() 
21138     {
21139         // this may only work on Gecko!!!
21140         
21141         // should we cache this!!!!
21142         
21143         
21144         
21145          
21146         var range = this.createRange(this.getSelection()).cloneRange();
21147         
21148         if (Roo.isIE) {
21149             var parent = range.parentElement();
21150             while (true) {
21151                 var testRange = range.duplicate();
21152                 testRange.moveToElementText(parent);
21153                 if (testRange.inRange(range)) {
21154                     break;
21155                 }
21156                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21157                     break;
21158                 }
21159                 parent = parent.parentElement;
21160             }
21161             return parent;
21162         }
21163         
21164         // is ancestor a text element.
21165         var ac =  range.commonAncestorContainer;
21166         if (ac.nodeType == 3) {
21167             ac = ac.parentNode;
21168         }
21169         
21170         var ar = ac.childNodes;
21171          
21172         var nodes = [];
21173         var other_nodes = [];
21174         var has_other_nodes = false;
21175         for (var i=0;i<ar.length;i++) {
21176             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21177                 continue;
21178             }
21179             // fullly contained node.
21180             
21181             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21182                 nodes.push(ar[i]);
21183                 continue;
21184             }
21185             
21186             // probably selected..
21187             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21188                 other_nodes.push(ar[i]);
21189                 continue;
21190             }
21191             // outer..
21192             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21193                 continue;
21194             }
21195             
21196             
21197             has_other_nodes = true;
21198         }
21199         if (!nodes.length && other_nodes.length) {
21200             nodes= other_nodes;
21201         }
21202         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21203             return false;
21204         }
21205         
21206         return nodes[0];
21207     },
21208     createRange: function(sel)
21209     {
21210         // this has strange effects when using with 
21211         // top toolbar - not sure if it's a great idea.
21212         //this.editor.contentWindow.focus();
21213         if (typeof sel != "undefined") {
21214             try {
21215                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21216             } catch(e) {
21217                 return this.doc.createRange();
21218             }
21219         } else {
21220             return this.doc.createRange();
21221         }
21222     },
21223     getParentElement: function()
21224     {
21225         
21226         this.assignDocWin();
21227         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21228         
21229         var range = this.createRange(sel);
21230          
21231         try {
21232             var p = range.commonAncestorContainer;
21233             while (p.nodeType == 3) { // text node
21234                 p = p.parentNode;
21235             }
21236             return p;
21237         } catch (e) {
21238             return null;
21239         }
21240     
21241     },
21242     /***
21243      *
21244      * Range intersection.. the hard stuff...
21245      *  '-1' = before
21246      *  '0' = hits..
21247      *  '1' = after.
21248      *         [ -- selected range --- ]
21249      *   [fail]                        [fail]
21250      *
21251      *    basically..
21252      *      if end is before start or  hits it. fail.
21253      *      if start is after end or hits it fail.
21254      *
21255      *   if either hits (but other is outside. - then it's not 
21256      *   
21257      *    
21258      **/
21259     
21260     
21261     // @see http://www.thismuchiknow.co.uk/?p=64.
21262     rangeIntersectsNode : function(range, node)
21263     {
21264         var nodeRange = node.ownerDocument.createRange();
21265         try {
21266             nodeRange.selectNode(node);
21267         } catch (e) {
21268             nodeRange.selectNodeContents(node);
21269         }
21270     
21271         var rangeStartRange = range.cloneRange();
21272         rangeStartRange.collapse(true);
21273     
21274         var rangeEndRange = range.cloneRange();
21275         rangeEndRange.collapse(false);
21276     
21277         var nodeStartRange = nodeRange.cloneRange();
21278         nodeStartRange.collapse(true);
21279     
21280         var nodeEndRange = nodeRange.cloneRange();
21281         nodeEndRange.collapse(false);
21282     
21283         return rangeStartRange.compareBoundaryPoints(
21284                  Range.START_TO_START, nodeEndRange) == -1 &&
21285                rangeEndRange.compareBoundaryPoints(
21286                  Range.START_TO_START, nodeStartRange) == 1;
21287         
21288          
21289     },
21290     rangeCompareNode : function(range, node)
21291     {
21292         var nodeRange = node.ownerDocument.createRange();
21293         try {
21294             nodeRange.selectNode(node);
21295         } catch (e) {
21296             nodeRange.selectNodeContents(node);
21297         }
21298         
21299         
21300         range.collapse(true);
21301     
21302         nodeRange.collapse(true);
21303      
21304         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21305         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21306          
21307         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21308         
21309         var nodeIsBefore   =  ss == 1;
21310         var nodeIsAfter    = ee == -1;
21311         
21312         if (nodeIsBefore && nodeIsAfter) {
21313             return 0; // outer
21314         }
21315         if (!nodeIsBefore && nodeIsAfter) {
21316             return 1; //right trailed.
21317         }
21318         
21319         if (nodeIsBefore && !nodeIsAfter) {
21320             return 2;  // left trailed.
21321         }
21322         // fully contined.
21323         return 3;
21324     },
21325
21326     // private? - in a new class?
21327     cleanUpPaste :  function()
21328     {
21329         // cleans up the whole document..
21330         Roo.log('cleanuppaste');
21331         
21332         this.cleanUpChildren(this.doc.body);
21333         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21334         if (clean != this.doc.body.innerHTML) {
21335             this.doc.body.innerHTML = clean;
21336         }
21337         
21338     },
21339     
21340     cleanWordChars : function(input) {// change the chars to hex code
21341         var he = Roo.HtmlEditorCore;
21342         
21343         var output = input;
21344         Roo.each(he.swapCodes, function(sw) { 
21345             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21346             
21347             output = output.replace(swapper, sw[1]);
21348         });
21349         
21350         return output;
21351     },
21352     
21353     
21354     cleanUpChildren : function (n)
21355     {
21356         if (!n.childNodes.length) {
21357             return;
21358         }
21359         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21360            this.cleanUpChild(n.childNodes[i]);
21361         }
21362     },
21363     
21364     
21365         
21366     
21367     cleanUpChild : function (node)
21368     {
21369         var ed = this;
21370         //console.log(node);
21371         if (node.nodeName == "#text") {
21372             // clean up silly Windows -- stuff?
21373             return; 
21374         }
21375         if (node.nodeName == "#comment") {
21376             node.parentNode.removeChild(node);
21377             // clean up silly Windows -- stuff?
21378             return; 
21379         }
21380         var lcname = node.tagName.toLowerCase();
21381         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21382         // whitelist of tags..
21383         
21384         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21385             // remove node.
21386             node.parentNode.removeChild(node);
21387             return;
21388             
21389         }
21390         
21391         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21392         
21393         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21394         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21395         
21396         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21397         //    remove_keep_children = true;
21398         //}
21399         
21400         if (remove_keep_children) {
21401             this.cleanUpChildren(node);
21402             // inserts everything just before this node...
21403             while (node.childNodes.length) {
21404                 var cn = node.childNodes[0];
21405                 node.removeChild(cn);
21406                 node.parentNode.insertBefore(cn, node);
21407             }
21408             node.parentNode.removeChild(node);
21409             return;
21410         }
21411         
21412         if (!node.attributes || !node.attributes.length) {
21413             this.cleanUpChildren(node);
21414             return;
21415         }
21416         
21417         function cleanAttr(n,v)
21418         {
21419             
21420             if (v.match(/^\./) || v.match(/^\//)) {
21421                 return;
21422             }
21423             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21424                 return;
21425             }
21426             if (v.match(/^#/)) {
21427                 return;
21428             }
21429 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21430             node.removeAttribute(n);
21431             
21432         }
21433         
21434         var cwhite = this.cwhite;
21435         var cblack = this.cblack;
21436             
21437         function cleanStyle(n,v)
21438         {
21439             if (v.match(/expression/)) { //XSS?? should we even bother..
21440                 node.removeAttribute(n);
21441                 return;
21442             }
21443             
21444             var parts = v.split(/;/);
21445             var clean = [];
21446             
21447             Roo.each(parts, function(p) {
21448                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21449                 if (!p.length) {
21450                     return true;
21451                 }
21452                 var l = p.split(':').shift().replace(/\s+/g,'');
21453                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21454                 
21455                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21456 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21457                     //node.removeAttribute(n);
21458                     return true;
21459                 }
21460                 //Roo.log()
21461                 // only allow 'c whitelisted system attributes'
21462                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21463 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21464                     //node.removeAttribute(n);
21465                     return true;
21466                 }
21467                 
21468                 
21469                  
21470                 
21471                 clean.push(p);
21472                 return true;
21473             });
21474             if (clean.length) { 
21475                 node.setAttribute(n, clean.join(';'));
21476             } else {
21477                 node.removeAttribute(n);
21478             }
21479             
21480         }
21481         
21482         
21483         for (var i = node.attributes.length-1; i > -1 ; i--) {
21484             var a = node.attributes[i];
21485             //console.log(a);
21486             
21487             if (a.name.toLowerCase().substr(0,2)=='on')  {
21488                 node.removeAttribute(a.name);
21489                 continue;
21490             }
21491             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21492                 node.removeAttribute(a.name);
21493                 continue;
21494             }
21495             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21496                 cleanAttr(a.name,a.value); // fixme..
21497                 continue;
21498             }
21499             if (a.name == 'style') {
21500                 cleanStyle(a.name,a.value);
21501                 continue;
21502             }
21503             /// clean up MS crap..
21504             // tecnically this should be a list of valid class'es..
21505             
21506             
21507             if (a.name == 'class') {
21508                 if (a.value.match(/^Mso/)) {
21509                     node.className = '';
21510                 }
21511                 
21512                 if (a.value.match(/^body$/)) {
21513                     node.className = '';
21514                 }
21515                 continue;
21516             }
21517             
21518             // style cleanup!?
21519             // class cleanup?
21520             
21521         }
21522         
21523         
21524         this.cleanUpChildren(node);
21525         
21526         
21527     },
21528     
21529     /**
21530      * Clean up MS wordisms...
21531      */
21532     cleanWord : function(node)
21533     {
21534         
21535         
21536         if (!node) {
21537             this.cleanWord(this.doc.body);
21538             return;
21539         }
21540         if (node.nodeName == "#text") {
21541             // clean up silly Windows -- stuff?
21542             return; 
21543         }
21544         if (node.nodeName == "#comment") {
21545             node.parentNode.removeChild(node);
21546             // clean up silly Windows -- stuff?
21547             return; 
21548         }
21549         
21550         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21551             node.parentNode.removeChild(node);
21552             return;
21553         }
21554         
21555         // remove - but keep children..
21556         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21557             while (node.childNodes.length) {
21558                 var cn = node.childNodes[0];
21559                 node.removeChild(cn);
21560                 node.parentNode.insertBefore(cn, node);
21561             }
21562             node.parentNode.removeChild(node);
21563             this.iterateChildren(node, this.cleanWord);
21564             return;
21565         }
21566         // clean styles
21567         if (node.className.length) {
21568             
21569             var cn = node.className.split(/\W+/);
21570             var cna = [];
21571             Roo.each(cn, function(cls) {
21572                 if (cls.match(/Mso[a-zA-Z]+/)) {
21573                     return;
21574                 }
21575                 cna.push(cls);
21576             });
21577             node.className = cna.length ? cna.join(' ') : '';
21578             if (!cna.length) {
21579                 node.removeAttribute("class");
21580             }
21581         }
21582         
21583         if (node.hasAttribute("lang")) {
21584             node.removeAttribute("lang");
21585         }
21586         
21587         if (node.hasAttribute("style")) {
21588             
21589             var styles = node.getAttribute("style").split(";");
21590             var nstyle = [];
21591             Roo.each(styles, function(s) {
21592                 if (!s.match(/:/)) {
21593                     return;
21594                 }
21595                 var kv = s.split(":");
21596                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21597                     return;
21598                 }
21599                 // what ever is left... we allow.
21600                 nstyle.push(s);
21601             });
21602             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21603             if (!nstyle.length) {
21604                 node.removeAttribute('style');
21605             }
21606         }
21607         this.iterateChildren(node, this.cleanWord);
21608         
21609         
21610         
21611     },
21612     /**
21613      * iterateChildren of a Node, calling fn each time, using this as the scole..
21614      * @param {DomNode} node node to iterate children of.
21615      * @param {Function} fn method of this class to call on each item.
21616      */
21617     iterateChildren : function(node, fn)
21618     {
21619         if (!node.childNodes.length) {
21620                 return;
21621         }
21622         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21623            fn.call(this, node.childNodes[i])
21624         }
21625     },
21626     
21627     
21628     /**
21629      * cleanTableWidths.
21630      *
21631      * Quite often pasting from word etc.. results in tables with column and widths.
21632      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21633      *
21634      */
21635     cleanTableWidths : function(node)
21636     {
21637          
21638          
21639         if (!node) {
21640             this.cleanTableWidths(this.doc.body);
21641             return;
21642         }
21643         
21644         // ignore list...
21645         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21646             return; 
21647         }
21648         Roo.log(node.tagName);
21649         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21650             this.iterateChildren(node, this.cleanTableWidths);
21651             return;
21652         }
21653         if (node.hasAttribute('width')) {
21654             node.removeAttribute('width');
21655         }
21656         
21657          
21658         if (node.hasAttribute("style")) {
21659             // pretty basic...
21660             
21661             var styles = node.getAttribute("style").split(";");
21662             var nstyle = [];
21663             Roo.each(styles, function(s) {
21664                 if (!s.match(/:/)) {
21665                     return;
21666                 }
21667                 var kv = s.split(":");
21668                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21669                     return;
21670                 }
21671                 // what ever is left... we allow.
21672                 nstyle.push(s);
21673             });
21674             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21675             if (!nstyle.length) {
21676                 node.removeAttribute('style');
21677             }
21678         }
21679         
21680         this.iterateChildren(node, this.cleanTableWidths);
21681         
21682         
21683     },
21684     
21685     
21686     
21687     
21688     domToHTML : function(currentElement, depth, nopadtext) {
21689         
21690         depth = depth || 0;
21691         nopadtext = nopadtext || false;
21692     
21693         if (!currentElement) {
21694             return this.domToHTML(this.doc.body);
21695         }
21696         
21697         //Roo.log(currentElement);
21698         var j;
21699         var allText = false;
21700         var nodeName = currentElement.nodeName;
21701         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21702         
21703         if  (nodeName == '#text') {
21704             
21705             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21706         }
21707         
21708         
21709         var ret = '';
21710         if (nodeName != 'BODY') {
21711              
21712             var i = 0;
21713             // Prints the node tagName, such as <A>, <IMG>, etc
21714             if (tagName) {
21715                 var attr = [];
21716                 for(i = 0; i < currentElement.attributes.length;i++) {
21717                     // quoting?
21718                     var aname = currentElement.attributes.item(i).name;
21719                     if (!currentElement.attributes.item(i).value.length) {
21720                         continue;
21721                     }
21722                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21723                 }
21724                 
21725                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21726             } 
21727             else {
21728                 
21729                 // eack
21730             }
21731         } else {
21732             tagName = false;
21733         }
21734         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21735             return ret;
21736         }
21737         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21738             nopadtext = true;
21739         }
21740         
21741         
21742         // Traverse the tree
21743         i = 0;
21744         var currentElementChild = currentElement.childNodes.item(i);
21745         var allText = true;
21746         var innerHTML  = '';
21747         lastnode = '';
21748         while (currentElementChild) {
21749             // Formatting code (indent the tree so it looks nice on the screen)
21750             var nopad = nopadtext;
21751             if (lastnode == 'SPAN') {
21752                 nopad  = true;
21753             }
21754             // text
21755             if  (currentElementChild.nodeName == '#text') {
21756                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21757                 toadd = nopadtext ? toadd : toadd.trim();
21758                 if (!nopad && toadd.length > 80) {
21759                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21760                 }
21761                 innerHTML  += toadd;
21762                 
21763                 i++;
21764                 currentElementChild = currentElement.childNodes.item(i);
21765                 lastNode = '';
21766                 continue;
21767             }
21768             allText = false;
21769             
21770             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21771                 
21772             // Recursively traverse the tree structure of the child node
21773             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21774             lastnode = currentElementChild.nodeName;
21775             i++;
21776             currentElementChild=currentElement.childNodes.item(i);
21777         }
21778         
21779         ret += innerHTML;
21780         
21781         if (!allText) {
21782                 // The remaining code is mostly for formatting the tree
21783             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21784         }
21785         
21786         
21787         if (tagName) {
21788             ret+= "</"+tagName+">";
21789         }
21790         return ret;
21791         
21792     },
21793         
21794     applyBlacklists : function()
21795     {
21796         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21797         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21798         
21799         this.white = [];
21800         this.black = [];
21801         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21802             if (b.indexOf(tag) > -1) {
21803                 return;
21804             }
21805             this.white.push(tag);
21806             
21807         }, this);
21808         
21809         Roo.each(w, function(tag) {
21810             if (b.indexOf(tag) > -1) {
21811                 return;
21812             }
21813             if (this.white.indexOf(tag) > -1) {
21814                 return;
21815             }
21816             this.white.push(tag);
21817             
21818         }, this);
21819         
21820         
21821         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21822             if (w.indexOf(tag) > -1) {
21823                 return;
21824             }
21825             this.black.push(tag);
21826             
21827         }, this);
21828         
21829         Roo.each(b, function(tag) {
21830             if (w.indexOf(tag) > -1) {
21831                 return;
21832             }
21833             if (this.black.indexOf(tag) > -1) {
21834                 return;
21835             }
21836             this.black.push(tag);
21837             
21838         }, this);
21839         
21840         
21841         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21842         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21843         
21844         this.cwhite = [];
21845         this.cblack = [];
21846         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21847             if (b.indexOf(tag) > -1) {
21848                 return;
21849             }
21850             this.cwhite.push(tag);
21851             
21852         }, this);
21853         
21854         Roo.each(w, function(tag) {
21855             if (b.indexOf(tag) > -1) {
21856                 return;
21857             }
21858             if (this.cwhite.indexOf(tag) > -1) {
21859                 return;
21860             }
21861             this.cwhite.push(tag);
21862             
21863         }, this);
21864         
21865         
21866         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21867             if (w.indexOf(tag) > -1) {
21868                 return;
21869             }
21870             this.cblack.push(tag);
21871             
21872         }, this);
21873         
21874         Roo.each(b, function(tag) {
21875             if (w.indexOf(tag) > -1) {
21876                 return;
21877             }
21878             if (this.cblack.indexOf(tag) > -1) {
21879                 return;
21880             }
21881             this.cblack.push(tag);
21882             
21883         }, this);
21884     },
21885     
21886     setStylesheets : function(stylesheets)
21887     {
21888         if(typeof(stylesheets) == 'string'){
21889             Roo.get(this.iframe.contentDocument.head).createChild({
21890                 tag : 'link',
21891                 rel : 'stylesheet',
21892                 type : 'text/css',
21893                 href : stylesheets
21894             });
21895             
21896             return;
21897         }
21898         var _this = this;
21899      
21900         Roo.each(stylesheets, function(s) {
21901             if(!s.length){
21902                 return;
21903             }
21904             
21905             Roo.get(_this.iframe.contentDocument.head).createChild({
21906                 tag : 'link',
21907                 rel : 'stylesheet',
21908                 type : 'text/css',
21909                 href : s
21910             });
21911         });
21912
21913         
21914     },
21915     
21916     removeStylesheets : function()
21917     {
21918         var _this = this;
21919         
21920         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21921             s.remove();
21922         });
21923     },
21924     
21925     setStyle : function(style)
21926     {
21927         Roo.get(this.iframe.contentDocument.head).createChild({
21928             tag : 'style',
21929             type : 'text/css',
21930             html : style
21931         });
21932
21933         return;
21934     }
21935     
21936     // hide stuff that is not compatible
21937     /**
21938      * @event blur
21939      * @hide
21940      */
21941     /**
21942      * @event change
21943      * @hide
21944      */
21945     /**
21946      * @event focus
21947      * @hide
21948      */
21949     /**
21950      * @event specialkey
21951      * @hide
21952      */
21953     /**
21954      * @cfg {String} fieldClass @hide
21955      */
21956     /**
21957      * @cfg {String} focusClass @hide
21958      */
21959     /**
21960      * @cfg {String} autoCreate @hide
21961      */
21962     /**
21963      * @cfg {String} inputType @hide
21964      */
21965     /**
21966      * @cfg {String} invalidClass @hide
21967      */
21968     /**
21969      * @cfg {String} invalidText @hide
21970      */
21971     /**
21972      * @cfg {String} msgFx @hide
21973      */
21974     /**
21975      * @cfg {String} validateOnBlur @hide
21976      */
21977 });
21978
21979 Roo.HtmlEditorCore.white = [
21980         'area', 'br', 'img', 'input', 'hr', 'wbr',
21981         
21982        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21983        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21984        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21985        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21986        'table',   'ul',         'xmp', 
21987        
21988        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21989       'thead',   'tr', 
21990      
21991       'dir', 'menu', 'ol', 'ul', 'dl',
21992        
21993       'embed',  'object'
21994 ];
21995
21996
21997 Roo.HtmlEditorCore.black = [
21998     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21999         'applet', // 
22000         'base',   'basefont', 'bgsound', 'blink',  'body', 
22001         'frame',  'frameset', 'head',    'html',   'ilayer', 
22002         'iframe', 'layer',  'link',     'meta',    'object',   
22003         'script', 'style' ,'title',  'xml' // clean later..
22004 ];
22005 Roo.HtmlEditorCore.clean = [
22006     'script', 'style', 'title', 'xml'
22007 ];
22008 Roo.HtmlEditorCore.remove = [
22009     'font'
22010 ];
22011 // attributes..
22012
22013 Roo.HtmlEditorCore.ablack = [
22014     'on'
22015 ];
22016     
22017 Roo.HtmlEditorCore.aclean = [ 
22018     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22019 ];
22020
22021 // protocols..
22022 Roo.HtmlEditorCore.pwhite= [
22023         'http',  'https',  'mailto'
22024 ];
22025
22026 // white listed style attributes.
22027 Roo.HtmlEditorCore.cwhite= [
22028       //  'text-align', /// default is to allow most things..
22029       
22030          
22031 //        'font-size'//??
22032 ];
22033
22034 // black listed style attributes.
22035 Roo.HtmlEditorCore.cblack= [
22036       //  'font-size' -- this can be set by the project 
22037 ];
22038
22039
22040 Roo.HtmlEditorCore.swapCodes   =[ 
22041     [    8211, "--" ], 
22042     [    8212, "--" ], 
22043     [    8216,  "'" ],  
22044     [    8217, "'" ],  
22045     [    8220, '"' ],  
22046     [    8221, '"' ],  
22047     [    8226, "*" ],  
22048     [    8230, "..." ]
22049 ]; 
22050
22051     //<script type="text/javascript">
22052
22053 /*
22054  * Ext JS Library 1.1.1
22055  * Copyright(c) 2006-2007, Ext JS, LLC.
22056  * Licence LGPL
22057  * 
22058  */
22059  
22060  
22061 Roo.form.HtmlEditor = function(config){
22062     
22063     
22064     
22065     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22066     
22067     if (!this.toolbars) {
22068         this.toolbars = [];
22069     }
22070     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22071     
22072     
22073 };
22074
22075 /**
22076  * @class Roo.form.HtmlEditor
22077  * @extends Roo.form.Field
22078  * Provides a lightweight HTML Editor component.
22079  *
22080  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22081  * 
22082  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22083  * supported by this editor.</b><br/><br/>
22084  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22085  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22086  */
22087 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22088     /**
22089      * @cfg {Boolean} clearUp
22090      */
22091     clearUp : true,
22092       /**
22093      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22094      */
22095     toolbars : false,
22096    
22097      /**
22098      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22099      *                        Roo.resizable.
22100      */
22101     resizable : false,
22102      /**
22103      * @cfg {Number} height (in pixels)
22104      */   
22105     height: 300,
22106    /**
22107      * @cfg {Number} width (in pixels)
22108      */   
22109     width: 500,
22110     
22111     /**
22112      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22113      * 
22114      */
22115     stylesheets: false,
22116     
22117     
22118      /**
22119      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22120      * 
22121      */
22122     cblack: false,
22123     /**
22124      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22125      * 
22126      */
22127     cwhite: false,
22128     
22129      /**
22130      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22131      * 
22132      */
22133     black: false,
22134     /**
22135      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22136      * 
22137      */
22138     white: false,
22139     
22140     // id of frame..
22141     frameId: false,
22142     
22143     // private properties
22144     validationEvent : false,
22145     deferHeight: true,
22146     initialized : false,
22147     activated : false,
22148     
22149     onFocus : Roo.emptyFn,
22150     iframePad:3,
22151     hideMode:'offsets',
22152     
22153     actionMode : 'container', // defaults to hiding it...
22154     
22155     defaultAutoCreate : { // modified by initCompnoent..
22156         tag: "textarea",
22157         style:"width:500px;height:300px;",
22158         autocomplete: "new-password"
22159     },
22160
22161     // private
22162     initComponent : function(){
22163         this.addEvents({
22164             /**
22165              * @event initialize
22166              * Fires when the editor is fully initialized (including the iframe)
22167              * @param {HtmlEditor} this
22168              */
22169             initialize: true,
22170             /**
22171              * @event activate
22172              * Fires when the editor is first receives the focus. Any insertion must wait
22173              * until after this event.
22174              * @param {HtmlEditor} this
22175              */
22176             activate: true,
22177              /**
22178              * @event beforesync
22179              * Fires before the textarea is updated with content from the editor iframe. Return false
22180              * to cancel the sync.
22181              * @param {HtmlEditor} this
22182              * @param {String} html
22183              */
22184             beforesync: true,
22185              /**
22186              * @event beforepush
22187              * Fires before the iframe editor is updated with content from the textarea. Return false
22188              * to cancel the push.
22189              * @param {HtmlEditor} this
22190              * @param {String} html
22191              */
22192             beforepush: true,
22193              /**
22194              * @event sync
22195              * Fires when the textarea is updated with content from the editor iframe.
22196              * @param {HtmlEditor} this
22197              * @param {String} html
22198              */
22199             sync: true,
22200              /**
22201              * @event push
22202              * Fires when the iframe editor is updated with content from the textarea.
22203              * @param {HtmlEditor} this
22204              * @param {String} html
22205              */
22206             push: true,
22207              /**
22208              * @event editmodechange
22209              * Fires when the editor switches edit modes
22210              * @param {HtmlEditor} this
22211              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22212              */
22213             editmodechange: true,
22214             /**
22215              * @event editorevent
22216              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22217              * @param {HtmlEditor} this
22218              */
22219             editorevent: true,
22220             /**
22221              * @event firstfocus
22222              * Fires when on first focus - needed by toolbars..
22223              * @param {HtmlEditor} this
22224              */
22225             firstfocus: true,
22226             /**
22227              * @event autosave
22228              * Auto save the htmlEditor value as a file into Events
22229              * @param {HtmlEditor} this
22230              */
22231             autosave: true,
22232             /**
22233              * @event savedpreview
22234              * preview the saved version of htmlEditor
22235              * @param {HtmlEditor} this
22236              */
22237             savedpreview: true,
22238             
22239             /**
22240             * @event stylesheetsclick
22241             * Fires when press the Sytlesheets button
22242             * @param {Roo.HtmlEditorCore} this
22243             */
22244             stylesheetsclick: true
22245         });
22246         this.defaultAutoCreate =  {
22247             tag: "textarea",
22248             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22249             autocomplete: "new-password"
22250         };
22251     },
22252
22253     /**
22254      * Protected method that will not generally be called directly. It
22255      * is called when the editor creates its toolbar. Override this method if you need to
22256      * add custom toolbar buttons.
22257      * @param {HtmlEditor} editor
22258      */
22259     createToolbar : function(editor){
22260         Roo.log("create toolbars");
22261         if (!editor.toolbars || !editor.toolbars.length) {
22262             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22263         }
22264         
22265         for (var i =0 ; i < editor.toolbars.length;i++) {
22266             editor.toolbars[i] = Roo.factory(
22267                     typeof(editor.toolbars[i]) == 'string' ?
22268                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22269                 Roo.form.HtmlEditor);
22270             editor.toolbars[i].init(editor);
22271         }
22272          
22273         
22274     },
22275
22276      
22277     // private
22278     onRender : function(ct, position)
22279     {
22280         var _t = this;
22281         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22282         
22283         this.wrap = this.el.wrap({
22284             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22285         });
22286         
22287         this.editorcore.onRender(ct, position);
22288          
22289         if (this.resizable) {
22290             this.resizeEl = new Roo.Resizable(this.wrap, {
22291                 pinned : true,
22292                 wrap: true,
22293                 dynamic : true,
22294                 minHeight : this.height,
22295                 height: this.height,
22296                 handles : this.resizable,
22297                 width: this.width,
22298                 listeners : {
22299                     resize : function(r, w, h) {
22300                         _t.onResize(w,h); // -something
22301                     }
22302                 }
22303             });
22304             
22305         }
22306         this.createToolbar(this);
22307        
22308         
22309         if(!this.width){
22310             this.setSize(this.wrap.getSize());
22311         }
22312         if (this.resizeEl) {
22313             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22314             // should trigger onReize..
22315         }
22316         
22317         this.keyNav = new Roo.KeyNav(this.el, {
22318             
22319             "tab" : function(e){
22320                 e.preventDefault();
22321                 
22322                 var value = this.getValue();
22323                 
22324                 var start = this.el.dom.selectionStart;
22325                 var end = this.el.dom.selectionEnd;
22326                 
22327                 if(!e.shiftKey){
22328                     
22329                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22330                     this.el.dom.setSelectionRange(end + 1, end + 1);
22331                     return;
22332                 }
22333                 
22334                 var f = value.substring(0, start).split("\t");
22335                 
22336                 if(f.pop().length != 0){
22337                     return;
22338                 }
22339                 
22340                 this.setValue(f.join("\t") + value.substring(end));
22341                 this.el.dom.setSelectionRange(start - 1, start - 1);
22342                 
22343             },
22344             
22345             "home" : function(e){
22346                 e.preventDefault();
22347                 
22348                 var curr = this.el.dom.selectionStart;
22349                 var lines = this.getValue().split("\n");
22350                 
22351                 if(!lines.length){
22352                     return;
22353                 }
22354                 
22355                 if(e.ctrlKey){
22356                     this.el.dom.setSelectionRange(0, 0);
22357                     return;
22358                 }
22359                 
22360                 var pos = 0;
22361                 
22362                 for (var i = 0; i < lines.length;i++) {
22363                     pos += lines[i].length;
22364                     
22365                     if(i != 0){
22366                         pos += 1;
22367                     }
22368                     
22369                     if(pos < curr){
22370                         continue;
22371                     }
22372                     
22373                     pos -= lines[i].length;
22374                     
22375                     break;
22376                 }
22377                 
22378                 if(!e.shiftKey){
22379                     this.el.dom.setSelectionRange(pos, pos);
22380                     return;
22381                 }
22382                 
22383                 this.el.dom.selectionStart = pos;
22384                 this.el.dom.selectionEnd = curr;
22385             },
22386             
22387             "end" : function(e){
22388                 e.preventDefault();
22389                 
22390                 var curr = this.el.dom.selectionStart;
22391                 var lines = this.getValue().split("\n");
22392                 
22393                 if(!lines.length){
22394                     return;
22395                 }
22396                 
22397                 if(e.ctrlKey){
22398                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22399                     return;
22400                 }
22401                 
22402                 var pos = 0;
22403                 
22404                 for (var i = 0; i < lines.length;i++) {
22405                     
22406                     pos += lines[i].length;
22407                     
22408                     if(i != 0){
22409                         pos += 1;
22410                     }
22411                     
22412                     if(pos < curr){
22413                         continue;
22414                     }
22415                     
22416                     break;
22417                 }
22418                 
22419                 if(!e.shiftKey){
22420                     this.el.dom.setSelectionRange(pos, pos);
22421                     return;
22422                 }
22423                 
22424                 this.el.dom.selectionStart = curr;
22425                 this.el.dom.selectionEnd = pos;
22426             },
22427
22428             scope : this,
22429
22430             doRelay : function(foo, bar, hname){
22431                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22432             },
22433
22434             forceKeyDown: true
22435         });
22436         
22437 //        if(this.autosave && this.w){
22438 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22439 //        }
22440     },
22441
22442     // private
22443     onResize : function(w, h)
22444     {
22445         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22446         var ew = false;
22447         var eh = false;
22448         
22449         if(this.el ){
22450             if(typeof w == 'number'){
22451                 var aw = w - this.wrap.getFrameWidth('lr');
22452                 this.el.setWidth(this.adjustWidth('textarea', aw));
22453                 ew = aw;
22454             }
22455             if(typeof h == 'number'){
22456                 var tbh = 0;
22457                 for (var i =0; i < this.toolbars.length;i++) {
22458                     // fixme - ask toolbars for heights?
22459                     tbh += this.toolbars[i].tb.el.getHeight();
22460                     if (this.toolbars[i].footer) {
22461                         tbh += this.toolbars[i].footer.el.getHeight();
22462                     }
22463                 }
22464                 
22465                 
22466                 
22467                 
22468                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22469                 ah -= 5; // knock a few pixes off for look..
22470 //                Roo.log(ah);
22471                 this.el.setHeight(this.adjustWidth('textarea', ah));
22472                 var eh = ah;
22473             }
22474         }
22475         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22476         this.editorcore.onResize(ew,eh);
22477         
22478     },
22479
22480     /**
22481      * Toggles the editor between standard and source edit mode.
22482      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22483      */
22484     toggleSourceEdit : function(sourceEditMode)
22485     {
22486         this.editorcore.toggleSourceEdit(sourceEditMode);
22487         
22488         if(this.editorcore.sourceEditMode){
22489             Roo.log('editor - showing textarea');
22490             
22491 //            Roo.log('in');
22492 //            Roo.log(this.syncValue());
22493             this.editorcore.syncValue();
22494             this.el.removeClass('x-hidden');
22495             this.el.dom.removeAttribute('tabIndex');
22496             this.el.focus();
22497             
22498             for (var i = 0; i < this.toolbars.length; i++) {
22499                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22500                     this.toolbars[i].tb.hide();
22501                     this.toolbars[i].footer.hide();
22502                 }
22503             }
22504             
22505         }else{
22506             Roo.log('editor - hiding textarea');
22507 //            Roo.log('out')
22508 //            Roo.log(this.pushValue()); 
22509             this.editorcore.pushValue();
22510             
22511             this.el.addClass('x-hidden');
22512             this.el.dom.setAttribute('tabIndex', -1);
22513             
22514             for (var i = 0; i < this.toolbars.length; i++) {
22515                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22516                     this.toolbars[i].tb.show();
22517                     this.toolbars[i].footer.show();
22518                 }
22519             }
22520             
22521             //this.deferFocus();
22522         }
22523         
22524         this.setSize(this.wrap.getSize());
22525         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22526         
22527         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22528     },
22529  
22530     // private (for BoxComponent)
22531     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22532
22533     // private (for BoxComponent)
22534     getResizeEl : function(){
22535         return this.wrap;
22536     },
22537
22538     // private (for BoxComponent)
22539     getPositionEl : function(){
22540         return this.wrap;
22541     },
22542
22543     // private
22544     initEvents : function(){
22545         this.originalValue = this.getValue();
22546     },
22547
22548     /**
22549      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22550      * @method
22551      */
22552     markInvalid : Roo.emptyFn,
22553     /**
22554      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22555      * @method
22556      */
22557     clearInvalid : Roo.emptyFn,
22558
22559     setValue : function(v){
22560         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22561         this.editorcore.pushValue();
22562     },
22563
22564      
22565     // private
22566     deferFocus : function(){
22567         this.focus.defer(10, this);
22568     },
22569
22570     // doc'ed in Field
22571     focus : function(){
22572         this.editorcore.focus();
22573         
22574     },
22575       
22576
22577     // private
22578     onDestroy : function(){
22579         
22580         
22581         
22582         if(this.rendered){
22583             
22584             for (var i =0; i < this.toolbars.length;i++) {
22585                 // fixme - ask toolbars for heights?
22586                 this.toolbars[i].onDestroy();
22587             }
22588             
22589             this.wrap.dom.innerHTML = '';
22590             this.wrap.remove();
22591         }
22592     },
22593
22594     // private
22595     onFirstFocus : function(){
22596         //Roo.log("onFirstFocus");
22597         this.editorcore.onFirstFocus();
22598          for (var i =0; i < this.toolbars.length;i++) {
22599             this.toolbars[i].onFirstFocus();
22600         }
22601         
22602     },
22603     
22604     // private
22605     syncValue : function()
22606     {
22607         this.editorcore.syncValue();
22608     },
22609     
22610     pushValue : function()
22611     {
22612         this.editorcore.pushValue();
22613     },
22614     
22615     setStylesheets : function(stylesheets)
22616     {
22617         this.editorcore.setStylesheets(stylesheets);
22618     },
22619     
22620     removeStylesheets : function()
22621     {
22622         this.editorcore.removeStylesheets();
22623     }
22624      
22625     
22626     // hide stuff that is not compatible
22627     /**
22628      * @event blur
22629      * @hide
22630      */
22631     /**
22632      * @event change
22633      * @hide
22634      */
22635     /**
22636      * @event focus
22637      * @hide
22638      */
22639     /**
22640      * @event specialkey
22641      * @hide
22642      */
22643     /**
22644      * @cfg {String} fieldClass @hide
22645      */
22646     /**
22647      * @cfg {String} focusClass @hide
22648      */
22649     /**
22650      * @cfg {String} autoCreate @hide
22651      */
22652     /**
22653      * @cfg {String} inputType @hide
22654      */
22655     /**
22656      * @cfg {String} invalidClass @hide
22657      */
22658     /**
22659      * @cfg {String} invalidText @hide
22660      */
22661     /**
22662      * @cfg {String} msgFx @hide
22663      */
22664     /**
22665      * @cfg {String} validateOnBlur @hide
22666      */
22667 });
22668  
22669     // <script type="text/javascript">
22670 /*
22671  * Based on
22672  * Ext JS Library 1.1.1
22673  * Copyright(c) 2006-2007, Ext JS, LLC.
22674  *  
22675  
22676  */
22677
22678 /**
22679  * @class Roo.form.HtmlEditorToolbar1
22680  * Basic Toolbar
22681  * 
22682  * Usage:
22683  *
22684  new Roo.form.HtmlEditor({
22685     ....
22686     toolbars : [
22687         new Roo.form.HtmlEditorToolbar1({
22688             disable : { fonts: 1 , format: 1, ..., ... , ...],
22689             btns : [ .... ]
22690         })
22691     }
22692      
22693  * 
22694  * @cfg {Object} disable List of elements to disable..
22695  * @cfg {Array} btns List of additional buttons.
22696  * 
22697  * 
22698  * NEEDS Extra CSS? 
22699  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22700  */
22701  
22702 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22703 {
22704     
22705     Roo.apply(this, config);
22706     
22707     // default disabled, based on 'good practice'..
22708     this.disable = this.disable || {};
22709     Roo.applyIf(this.disable, {
22710         fontSize : true,
22711         colors : true,
22712         specialElements : true
22713     });
22714     
22715     
22716     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22717     // dont call parent... till later.
22718 }
22719
22720 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22721     
22722     tb: false,
22723     
22724     rendered: false,
22725     
22726     editor : false,
22727     editorcore : false,
22728     /**
22729      * @cfg {Object} disable  List of toolbar elements to disable
22730          
22731      */
22732     disable : false,
22733     
22734     
22735      /**
22736      * @cfg {String} createLinkText The default text for the create link prompt
22737      */
22738     createLinkText : 'Please enter the URL for the link:',
22739     /**
22740      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22741      */
22742     defaultLinkValue : 'http:/'+'/',
22743    
22744     
22745       /**
22746      * @cfg {Array} fontFamilies An array of available font families
22747      */
22748     fontFamilies : [
22749         'Arial',
22750         'Courier New',
22751         'Tahoma',
22752         'Times New Roman',
22753         'Verdana'
22754     ],
22755     
22756     specialChars : [
22757            "&#169;",
22758           "&#174;",     
22759           "&#8482;",    
22760           "&#163;" ,    
22761          // "&#8212;",    
22762           "&#8230;",    
22763           "&#247;" ,    
22764         //  "&#225;" ,     ?? a acute?
22765            "&#8364;"    , //Euro
22766        //   "&#8220;"    ,
22767         //  "&#8221;"    ,
22768         //  "&#8226;"    ,
22769           "&#176;"  //   , // degrees
22770
22771          // "&#233;"     , // e ecute
22772          // "&#250;"     , // u ecute?
22773     ],
22774     
22775     specialElements : [
22776         {
22777             text: "Insert Table",
22778             xtype: 'MenuItem',
22779             xns : Roo.Menu,
22780             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22781                 
22782         },
22783         {    
22784             text: "Insert Image",
22785             xtype: 'MenuItem',
22786             xns : Roo.Menu,
22787             ihtml : '<img src="about:blank"/>'
22788             
22789         }
22790         
22791          
22792     ],
22793     
22794     
22795     inputElements : [ 
22796             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22797             "input:submit", "input:button", "select", "textarea", "label" ],
22798     formats : [
22799         ["p"] ,  
22800         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22801         ["pre"],[ "code"], 
22802         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22803         ['div'],['span']
22804     ],
22805     
22806     cleanStyles : [
22807         "font-size"
22808     ],
22809      /**
22810      * @cfg {String} defaultFont default font to use.
22811      */
22812     defaultFont: 'tahoma',
22813    
22814     fontSelect : false,
22815     
22816     
22817     formatCombo : false,
22818     
22819     init : function(editor)
22820     {
22821         this.editor = editor;
22822         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22823         var editorcore = this.editorcore;
22824         
22825         var _t = this;
22826         
22827         var fid = editorcore.frameId;
22828         var etb = this;
22829         function btn(id, toggle, handler){
22830             var xid = fid + '-'+ id ;
22831             return {
22832                 id : xid,
22833                 cmd : id,
22834                 cls : 'x-btn-icon x-edit-'+id,
22835                 enableToggle:toggle !== false,
22836                 scope: _t, // was editor...
22837                 handler:handler||_t.relayBtnCmd,
22838                 clickEvent:'mousedown',
22839                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22840                 tabIndex:-1
22841             };
22842         }
22843         
22844         
22845         
22846         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22847         this.tb = tb;
22848          // stop form submits
22849         tb.el.on('click', function(e){
22850             e.preventDefault(); // what does this do?
22851         });
22852
22853         if(!this.disable.font) { // && !Roo.isSafari){
22854             /* why no safari for fonts 
22855             editor.fontSelect = tb.el.createChild({
22856                 tag:'select',
22857                 tabIndex: -1,
22858                 cls:'x-font-select',
22859                 html: this.createFontOptions()
22860             });
22861             
22862             editor.fontSelect.on('change', function(){
22863                 var font = editor.fontSelect.dom.value;
22864                 editor.relayCmd('fontname', font);
22865                 editor.deferFocus();
22866             }, editor);
22867             
22868             tb.add(
22869                 editor.fontSelect.dom,
22870                 '-'
22871             );
22872             */
22873             
22874         };
22875         if(!this.disable.formats){
22876             this.formatCombo = new Roo.form.ComboBox({
22877                 store: new Roo.data.SimpleStore({
22878                     id : 'tag',
22879                     fields: ['tag'],
22880                     data : this.formats // from states.js
22881                 }),
22882                 blockFocus : true,
22883                 name : '',
22884                 //autoCreate : {tag: "div",  size: "20"},
22885                 displayField:'tag',
22886                 typeAhead: false,
22887                 mode: 'local',
22888                 editable : false,
22889                 triggerAction: 'all',
22890                 emptyText:'Add tag',
22891                 selectOnFocus:true,
22892                 width:135,
22893                 listeners : {
22894                     'select': function(c, r, i) {
22895                         editorcore.insertTag(r.get('tag'));
22896                         editor.focus();
22897                     }
22898                 }
22899
22900             });
22901             tb.addField(this.formatCombo);
22902             
22903         }
22904         
22905         if(!this.disable.format){
22906             tb.add(
22907                 btn('bold'),
22908                 btn('italic'),
22909                 btn('underline'),
22910                 btn('strikethrough')
22911             );
22912         };
22913         if(!this.disable.fontSize){
22914             tb.add(
22915                 '-',
22916                 
22917                 
22918                 btn('increasefontsize', false, editorcore.adjustFont),
22919                 btn('decreasefontsize', false, editorcore.adjustFont)
22920             );
22921         };
22922         
22923         
22924         if(!this.disable.colors){
22925             tb.add(
22926                 '-', {
22927                     id:editorcore.frameId +'-forecolor',
22928                     cls:'x-btn-icon x-edit-forecolor',
22929                     clickEvent:'mousedown',
22930                     tooltip: this.buttonTips['forecolor'] || undefined,
22931                     tabIndex:-1,
22932                     menu : new Roo.menu.ColorMenu({
22933                         allowReselect: true,
22934                         focus: Roo.emptyFn,
22935                         value:'000000',
22936                         plain:true,
22937                         selectHandler: function(cp, color){
22938                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22939                             editor.deferFocus();
22940                         },
22941                         scope: editorcore,
22942                         clickEvent:'mousedown'
22943                     })
22944                 }, {
22945                     id:editorcore.frameId +'backcolor',
22946                     cls:'x-btn-icon x-edit-backcolor',
22947                     clickEvent:'mousedown',
22948                     tooltip: this.buttonTips['backcolor'] || undefined,
22949                     tabIndex:-1,
22950                     menu : new Roo.menu.ColorMenu({
22951                         focus: Roo.emptyFn,
22952                         value:'FFFFFF',
22953                         plain:true,
22954                         allowReselect: true,
22955                         selectHandler: function(cp, color){
22956                             if(Roo.isGecko){
22957                                 editorcore.execCmd('useCSS', false);
22958                                 editorcore.execCmd('hilitecolor', color);
22959                                 editorcore.execCmd('useCSS', true);
22960                                 editor.deferFocus();
22961                             }else{
22962                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22963                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22964                                 editor.deferFocus();
22965                             }
22966                         },
22967                         scope:editorcore,
22968                         clickEvent:'mousedown'
22969                     })
22970                 }
22971             );
22972         };
22973         // now add all the items...
22974         
22975
22976         if(!this.disable.alignments){
22977             tb.add(
22978                 '-',
22979                 btn('justifyleft'),
22980                 btn('justifycenter'),
22981                 btn('justifyright')
22982             );
22983         };
22984
22985         //if(!Roo.isSafari){
22986             if(!this.disable.links){
22987                 tb.add(
22988                     '-',
22989                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22990                 );
22991             };
22992
22993             if(!this.disable.lists){
22994                 tb.add(
22995                     '-',
22996                     btn('insertorderedlist'),
22997                     btn('insertunorderedlist')
22998                 );
22999             }
23000             if(!this.disable.sourceEdit){
23001                 tb.add(
23002                     '-',
23003                     btn('sourceedit', true, function(btn){
23004                         this.toggleSourceEdit(btn.pressed);
23005                     })
23006                 );
23007             }
23008         //}
23009         
23010         var smenu = { };
23011         // special menu.. - needs to be tidied up..
23012         if (!this.disable.special) {
23013             smenu = {
23014                 text: "&#169;",
23015                 cls: 'x-edit-none',
23016                 
23017                 menu : {
23018                     items : []
23019                 }
23020             };
23021             for (var i =0; i < this.specialChars.length; i++) {
23022                 smenu.menu.items.push({
23023                     
23024                     html: this.specialChars[i],
23025                     handler: function(a,b) {
23026                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23027                         //editor.insertAtCursor(a.html);
23028                         
23029                     },
23030                     tabIndex:-1
23031                 });
23032             }
23033             
23034             
23035             tb.add(smenu);
23036             
23037             
23038         }
23039         
23040         var cmenu = { };
23041         if (!this.disable.cleanStyles) {
23042             cmenu = {
23043                 cls: 'x-btn-icon x-btn-clear',
23044                 
23045                 menu : {
23046                     items : []
23047                 }
23048             };
23049             for (var i =0; i < this.cleanStyles.length; i++) {
23050                 cmenu.menu.items.push({
23051                     actiontype : this.cleanStyles[i],
23052                     html: 'Remove ' + this.cleanStyles[i],
23053                     handler: function(a,b) {
23054 //                        Roo.log(a);
23055 //                        Roo.log(b);
23056                         var c = Roo.get(editorcore.doc.body);
23057                         c.select('[style]').each(function(s) {
23058                             s.dom.style.removeProperty(a.actiontype);
23059                         });
23060                         editorcore.syncValue();
23061                     },
23062                     tabIndex:-1
23063                 });
23064             }
23065              cmenu.menu.items.push({
23066                 actiontype : 'tablewidths',
23067                 html: 'Remove Table Widths',
23068                 handler: function(a,b) {
23069                     editorcore.cleanTableWidths();
23070                     editorcore.syncValue();
23071                 },
23072                 tabIndex:-1
23073             });
23074             cmenu.menu.items.push({
23075                 actiontype : 'word',
23076                 html: 'Remove MS Word Formating',
23077                 handler: function(a,b) {
23078                     editorcore.cleanWord();
23079                     editorcore.syncValue();
23080                 },
23081                 tabIndex:-1
23082             });
23083             
23084             cmenu.menu.items.push({
23085                 actiontype : 'all',
23086                 html: 'Remove All Styles',
23087                 handler: function(a,b) {
23088                     
23089                     var c = Roo.get(editorcore.doc.body);
23090                     c.select('[style]').each(function(s) {
23091                         s.dom.removeAttribute('style');
23092                     });
23093                     editorcore.syncValue();
23094                 },
23095                 tabIndex:-1
23096             });
23097             
23098             cmenu.menu.items.push({
23099                 actiontype : 'all',
23100                 html: 'Remove All CSS Classes',
23101                 handler: function(a,b) {
23102                     
23103                     var c = Roo.get(editorcore.doc.body);
23104                     c.select('[class]').each(function(s) {
23105                         s.dom.className = '';
23106                     });
23107                     editorcore.syncValue();
23108                 },
23109                 tabIndex:-1
23110             });
23111             
23112              cmenu.menu.items.push({
23113                 actiontype : 'tidy',
23114                 html: 'Tidy HTML Source',
23115                 handler: function(a,b) {
23116                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23117                     editorcore.syncValue();
23118                 },
23119                 tabIndex:-1
23120             });
23121             
23122             
23123             tb.add(cmenu);
23124         }
23125          
23126         if (!this.disable.specialElements) {
23127             var semenu = {
23128                 text: "Other;",
23129                 cls: 'x-edit-none',
23130                 menu : {
23131                     items : []
23132                 }
23133             };
23134             for (var i =0; i < this.specialElements.length; i++) {
23135                 semenu.menu.items.push(
23136                     Roo.apply({ 
23137                         handler: function(a,b) {
23138                             editor.insertAtCursor(this.ihtml);
23139                         }
23140                     }, this.specialElements[i])
23141                 );
23142                     
23143             }
23144             
23145             tb.add(semenu);
23146             
23147             
23148         }
23149          
23150         
23151         if (this.btns) {
23152             for(var i =0; i< this.btns.length;i++) {
23153                 var b = Roo.factory(this.btns[i],Roo.form);
23154                 b.cls =  'x-edit-none';
23155                 
23156                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23157                     b.cls += ' x-init-enable';
23158                 }
23159                 
23160                 b.scope = editorcore;
23161                 tb.add(b);
23162             }
23163         
23164         }
23165         
23166         
23167         
23168         // disable everything...
23169         
23170         this.tb.items.each(function(item){
23171             
23172            if(
23173                 item.id != editorcore.frameId+ '-sourceedit' && 
23174                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23175             ){
23176                 
23177                 item.disable();
23178             }
23179         });
23180         this.rendered = true;
23181         
23182         // the all the btns;
23183         editor.on('editorevent', this.updateToolbar, this);
23184         // other toolbars need to implement this..
23185         //editor.on('editmodechange', this.updateToolbar, this);
23186     },
23187     
23188     
23189     relayBtnCmd : function(btn) {
23190         this.editorcore.relayCmd(btn.cmd);
23191     },
23192     // private used internally
23193     createLink : function(){
23194         Roo.log("create link?");
23195         var url = prompt(this.createLinkText, this.defaultLinkValue);
23196         if(url && url != 'http:/'+'/'){
23197             this.editorcore.relayCmd('createlink', url);
23198         }
23199     },
23200
23201     
23202     /**
23203      * Protected method that will not generally be called directly. It triggers
23204      * a toolbar update by reading the markup state of the current selection in the editor.
23205      */
23206     updateToolbar: function(){
23207
23208         if(!this.editorcore.activated){
23209             this.editor.onFirstFocus();
23210             return;
23211         }
23212
23213         var btns = this.tb.items.map, 
23214             doc = this.editorcore.doc,
23215             frameId = this.editorcore.frameId;
23216
23217         if(!this.disable.font && !Roo.isSafari){
23218             /*
23219             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23220             if(name != this.fontSelect.dom.value){
23221                 this.fontSelect.dom.value = name;
23222             }
23223             */
23224         }
23225         if(!this.disable.format){
23226             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23227             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23228             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23229             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23230         }
23231         if(!this.disable.alignments){
23232             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23233             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23234             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23235         }
23236         if(!Roo.isSafari && !this.disable.lists){
23237             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23238             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23239         }
23240         
23241         var ans = this.editorcore.getAllAncestors();
23242         if (this.formatCombo) {
23243             
23244             
23245             var store = this.formatCombo.store;
23246             this.formatCombo.setValue("");
23247             for (var i =0; i < ans.length;i++) {
23248                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23249                     // select it..
23250                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23251                     break;
23252                 }
23253             }
23254         }
23255         
23256         
23257         
23258         // hides menus... - so this cant be on a menu...
23259         Roo.menu.MenuMgr.hideAll();
23260
23261         //this.editorsyncValue();
23262     },
23263    
23264     
23265     createFontOptions : function(){
23266         var buf = [], fs = this.fontFamilies, ff, lc;
23267         
23268         
23269         
23270         for(var i = 0, len = fs.length; i< len; i++){
23271             ff = fs[i];
23272             lc = ff.toLowerCase();
23273             buf.push(
23274                 '<option value="',lc,'" style="font-family:',ff,';"',
23275                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23276                     ff,
23277                 '</option>'
23278             );
23279         }
23280         return buf.join('');
23281     },
23282     
23283     toggleSourceEdit : function(sourceEditMode){
23284         
23285         Roo.log("toolbar toogle");
23286         if(sourceEditMode === undefined){
23287             sourceEditMode = !this.sourceEditMode;
23288         }
23289         this.sourceEditMode = sourceEditMode === true;
23290         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23291         // just toggle the button?
23292         if(btn.pressed !== this.sourceEditMode){
23293             btn.toggle(this.sourceEditMode);
23294             return;
23295         }
23296         
23297         if(sourceEditMode){
23298             Roo.log("disabling buttons");
23299             this.tb.items.each(function(item){
23300                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23301                     item.disable();
23302                 }
23303             });
23304           
23305         }else{
23306             Roo.log("enabling buttons");
23307             if(this.editorcore.initialized){
23308                 this.tb.items.each(function(item){
23309                     item.enable();
23310                 });
23311             }
23312             
23313         }
23314         Roo.log("calling toggole on editor");
23315         // tell the editor that it's been pressed..
23316         this.editor.toggleSourceEdit(sourceEditMode);
23317        
23318     },
23319      /**
23320      * Object collection of toolbar tooltips for the buttons in the editor. The key
23321      * is the command id associated with that button and the value is a valid QuickTips object.
23322      * For example:
23323 <pre><code>
23324 {
23325     bold : {
23326         title: 'Bold (Ctrl+B)',
23327         text: 'Make the selected text bold.',
23328         cls: 'x-html-editor-tip'
23329     },
23330     italic : {
23331         title: 'Italic (Ctrl+I)',
23332         text: 'Make the selected text italic.',
23333         cls: 'x-html-editor-tip'
23334     },
23335     ...
23336 </code></pre>
23337     * @type Object
23338      */
23339     buttonTips : {
23340         bold : {
23341             title: 'Bold (Ctrl+B)',
23342             text: 'Make the selected text bold.',
23343             cls: 'x-html-editor-tip'
23344         },
23345         italic : {
23346             title: 'Italic (Ctrl+I)',
23347             text: 'Make the selected text italic.',
23348             cls: 'x-html-editor-tip'
23349         },
23350         underline : {
23351             title: 'Underline (Ctrl+U)',
23352             text: 'Underline the selected text.',
23353             cls: 'x-html-editor-tip'
23354         },
23355         strikethrough : {
23356             title: 'Strikethrough',
23357             text: 'Strikethrough the selected text.',
23358             cls: 'x-html-editor-tip'
23359         },
23360         increasefontsize : {
23361             title: 'Grow Text',
23362             text: 'Increase the font size.',
23363             cls: 'x-html-editor-tip'
23364         },
23365         decreasefontsize : {
23366             title: 'Shrink Text',
23367             text: 'Decrease the font size.',
23368             cls: 'x-html-editor-tip'
23369         },
23370         backcolor : {
23371             title: 'Text Highlight Color',
23372             text: 'Change the background color of the selected text.',
23373             cls: 'x-html-editor-tip'
23374         },
23375         forecolor : {
23376             title: 'Font Color',
23377             text: 'Change the color of the selected text.',
23378             cls: 'x-html-editor-tip'
23379         },
23380         justifyleft : {
23381             title: 'Align Text Left',
23382             text: 'Align text to the left.',
23383             cls: 'x-html-editor-tip'
23384         },
23385         justifycenter : {
23386             title: 'Center Text',
23387             text: 'Center text in the editor.',
23388             cls: 'x-html-editor-tip'
23389         },
23390         justifyright : {
23391             title: 'Align Text Right',
23392             text: 'Align text to the right.',
23393             cls: 'x-html-editor-tip'
23394         },
23395         insertunorderedlist : {
23396             title: 'Bullet List',
23397             text: 'Start a bulleted list.',
23398             cls: 'x-html-editor-tip'
23399         },
23400         insertorderedlist : {
23401             title: 'Numbered List',
23402             text: 'Start a numbered list.',
23403             cls: 'x-html-editor-tip'
23404         },
23405         createlink : {
23406             title: 'Hyperlink',
23407             text: 'Make the selected text a hyperlink.',
23408             cls: 'x-html-editor-tip'
23409         },
23410         sourceedit : {
23411             title: 'Source Edit',
23412             text: 'Switch to source editing mode.',
23413             cls: 'x-html-editor-tip'
23414         }
23415     },
23416     // private
23417     onDestroy : function(){
23418         if(this.rendered){
23419             
23420             this.tb.items.each(function(item){
23421                 if(item.menu){
23422                     item.menu.removeAll();
23423                     if(item.menu.el){
23424                         item.menu.el.destroy();
23425                     }
23426                 }
23427                 item.destroy();
23428             });
23429              
23430         }
23431     },
23432     onFirstFocus: function() {
23433         this.tb.items.each(function(item){
23434            item.enable();
23435         });
23436     }
23437 });
23438
23439
23440
23441
23442 // <script type="text/javascript">
23443 /*
23444  * Based on
23445  * Ext JS Library 1.1.1
23446  * Copyright(c) 2006-2007, Ext JS, LLC.
23447  *  
23448  
23449  */
23450
23451  
23452 /**
23453  * @class Roo.form.HtmlEditor.ToolbarContext
23454  * Context Toolbar
23455  * 
23456  * Usage:
23457  *
23458  new Roo.form.HtmlEditor({
23459     ....
23460     toolbars : [
23461         { xtype: 'ToolbarStandard', styles : {} }
23462         { xtype: 'ToolbarContext', disable : {} }
23463     ]
23464 })
23465
23466      
23467  * 
23468  * @config : {Object} disable List of elements to disable.. (not done yet.)
23469  * @config : {Object} styles  Map of styles available.
23470  * 
23471  */
23472
23473 Roo.form.HtmlEditor.ToolbarContext = function(config)
23474 {
23475     
23476     Roo.apply(this, config);
23477     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23478     // dont call parent... till later.
23479     this.styles = this.styles || {};
23480 }
23481
23482  
23483
23484 Roo.form.HtmlEditor.ToolbarContext.types = {
23485     'IMG' : {
23486         width : {
23487             title: "Width",
23488             width: 40
23489         },
23490         height:  {
23491             title: "Height",
23492             width: 40
23493         },
23494         align: {
23495             title: "Align",
23496             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23497             width : 80
23498             
23499         },
23500         border: {
23501             title: "Border",
23502             width: 40
23503         },
23504         alt: {
23505             title: "Alt",
23506             width: 120
23507         },
23508         src : {
23509             title: "Src",
23510             width: 220
23511         }
23512         
23513     },
23514     'A' : {
23515         name : {
23516             title: "Name",
23517             width: 50
23518         },
23519         target:  {
23520             title: "Target",
23521             width: 120
23522         },
23523         href:  {
23524             title: "Href",
23525             width: 220
23526         } // border?
23527         
23528     },
23529     'TABLE' : {
23530         rows : {
23531             title: "Rows",
23532             width: 20
23533         },
23534         cols : {
23535             title: "Cols",
23536             width: 20
23537         },
23538         width : {
23539             title: "Width",
23540             width: 40
23541         },
23542         height : {
23543             title: "Height",
23544             width: 40
23545         },
23546         border : {
23547             title: "Border",
23548             width: 20
23549         }
23550     },
23551     'TD' : {
23552         width : {
23553             title: "Width",
23554             width: 40
23555         },
23556         height : {
23557             title: "Height",
23558             width: 40
23559         },   
23560         align: {
23561             title: "Align",
23562             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23563             width: 80
23564         },
23565         valign: {
23566             title: "Valign",
23567             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23568             width: 80
23569         },
23570         colspan: {
23571             title: "Colspan",
23572             width: 20
23573             
23574         },
23575          'font-family'  : {
23576             title : "Font",
23577             style : 'fontFamily',
23578             displayField: 'display',
23579             optname : 'font-family',
23580             width: 140
23581         }
23582     },
23583     'INPUT' : {
23584         name : {
23585             title: "name",
23586             width: 120
23587         },
23588         value : {
23589             title: "Value",
23590             width: 120
23591         },
23592         width : {
23593             title: "Width",
23594             width: 40
23595         }
23596     },
23597     'LABEL' : {
23598         'for' : {
23599             title: "For",
23600             width: 120
23601         }
23602     },
23603     'TEXTAREA' : {
23604           name : {
23605             title: "name",
23606             width: 120
23607         },
23608         rows : {
23609             title: "Rows",
23610             width: 20
23611         },
23612         cols : {
23613             title: "Cols",
23614             width: 20
23615         }
23616     },
23617     'SELECT' : {
23618         name : {
23619             title: "name",
23620             width: 120
23621         },
23622         selectoptions : {
23623             title: "Options",
23624             width: 200
23625         }
23626     },
23627     
23628     // should we really allow this??
23629     // should this just be 
23630     'BODY' : {
23631         title : {
23632             title: "Title",
23633             width: 200,
23634             disabled : true
23635         }
23636     },
23637     'SPAN' : {
23638         'font-family'  : {
23639             title : "Font",
23640             style : 'fontFamily',
23641             displayField: 'display',
23642             optname : 'font-family',
23643             width: 140
23644         }
23645     },
23646     'DIV' : {
23647         'font-family'  : {
23648             title : "Font",
23649             style : 'fontFamily',
23650             displayField: 'display',
23651             optname : 'font-family',
23652             width: 140
23653         }
23654     },
23655      'P' : {
23656         'font-family'  : {
23657             title : "Font",
23658             style : 'fontFamily',
23659             displayField: 'display',
23660             optname : 'font-family',
23661             width: 140
23662         }
23663     },
23664     
23665     '*' : {
23666         // empty..
23667     }
23668
23669 };
23670
23671 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23672 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23673
23674 Roo.form.HtmlEditor.ToolbarContext.options = {
23675         'font-family'  : [ 
23676                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23677                 [ 'Courier New', 'Courier New'],
23678                 [ 'Tahoma', 'Tahoma'],
23679                 [ 'Times New Roman,serif', 'Times'],
23680                 [ 'Verdana','Verdana' ]
23681         ]
23682 };
23683
23684 // fixme - these need to be configurable..
23685  
23686
23687 //Roo.form.HtmlEditor.ToolbarContext.types
23688
23689
23690 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23691     
23692     tb: false,
23693     
23694     rendered: false,
23695     
23696     editor : false,
23697     editorcore : false,
23698     /**
23699      * @cfg {Object} disable  List of toolbar elements to disable
23700          
23701      */
23702     disable : false,
23703     /**
23704      * @cfg {Object} styles List of styles 
23705      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23706      *
23707      * These must be defined in the page, so they get rendered correctly..
23708      * .headline { }
23709      * TD.underline { }
23710      * 
23711      */
23712     styles : false,
23713     
23714     options: false,
23715     
23716     toolbars : false,
23717     
23718     init : function(editor)
23719     {
23720         this.editor = editor;
23721         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23722         var editorcore = this.editorcore;
23723         
23724         var fid = editorcore.frameId;
23725         var etb = this;
23726         function btn(id, toggle, handler){
23727             var xid = fid + '-'+ id ;
23728             return {
23729                 id : xid,
23730                 cmd : id,
23731                 cls : 'x-btn-icon x-edit-'+id,
23732                 enableToggle:toggle !== false,
23733                 scope: editorcore, // was editor...
23734                 handler:handler||editorcore.relayBtnCmd,
23735                 clickEvent:'mousedown',
23736                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23737                 tabIndex:-1
23738             };
23739         }
23740         // create a new element.
23741         var wdiv = editor.wrap.createChild({
23742                 tag: 'div'
23743             }, editor.wrap.dom.firstChild.nextSibling, true);
23744         
23745         // can we do this more than once??
23746         
23747          // stop form submits
23748       
23749  
23750         // disable everything...
23751         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23752         this.toolbars = {};
23753            
23754         for (var i in  ty) {
23755           
23756             this.toolbars[i] = this.buildToolbar(ty[i],i);
23757         }
23758         this.tb = this.toolbars.BODY;
23759         this.tb.el.show();
23760         this.buildFooter();
23761         this.footer.show();
23762         editor.on('hide', function( ) { this.footer.hide() }, this);
23763         editor.on('show', function( ) { this.footer.show() }, this);
23764         
23765          
23766         this.rendered = true;
23767         
23768         // the all the btns;
23769         editor.on('editorevent', this.updateToolbar, this);
23770         // other toolbars need to implement this..
23771         //editor.on('editmodechange', this.updateToolbar, this);
23772     },
23773     
23774     
23775     
23776     /**
23777      * Protected method that will not generally be called directly. It triggers
23778      * a toolbar update by reading the markup state of the current selection in the editor.
23779      *
23780      * Note you can force an update by calling on('editorevent', scope, false)
23781      */
23782     updateToolbar: function(editor,ev,sel){
23783
23784         //Roo.log(ev);
23785         // capture mouse up - this is handy for selecting images..
23786         // perhaps should go somewhere else...
23787         if(!this.editorcore.activated){
23788              this.editor.onFirstFocus();
23789             return;
23790         }
23791         
23792         
23793         
23794         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23795         // selectNode - might want to handle IE?
23796         if (ev &&
23797             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23798             ev.target && ev.target.tagName == 'IMG') {
23799             // they have click on an image...
23800             // let's see if we can change the selection...
23801             sel = ev.target;
23802          
23803               var nodeRange = sel.ownerDocument.createRange();
23804             try {
23805                 nodeRange.selectNode(sel);
23806             } catch (e) {
23807                 nodeRange.selectNodeContents(sel);
23808             }
23809             //nodeRange.collapse(true);
23810             var s = this.editorcore.win.getSelection();
23811             s.removeAllRanges();
23812             s.addRange(nodeRange);
23813         }  
23814         
23815       
23816         var updateFooter = sel ? false : true;
23817         
23818         
23819         var ans = this.editorcore.getAllAncestors();
23820         
23821         // pick
23822         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23823         
23824         if (!sel) { 
23825             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23826             sel = sel ? sel : this.editorcore.doc.body;
23827             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23828             
23829         }
23830         // pick a menu that exists..
23831         var tn = sel.tagName.toUpperCase();
23832         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23833         
23834         tn = sel.tagName.toUpperCase();
23835         
23836         var lastSel = this.tb.selectedNode;
23837         
23838         this.tb.selectedNode = sel;
23839         
23840         // if current menu does not match..
23841         
23842         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23843                 
23844             this.tb.el.hide();
23845             ///console.log("show: " + tn);
23846             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23847             this.tb.el.show();
23848             // update name
23849             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23850             
23851             
23852             // update attributes
23853             if (this.tb.fields) {
23854                 this.tb.fields.each(function(e) {
23855                     if (e.stylename) {
23856                         e.setValue(sel.style[e.stylename]);
23857                         return;
23858                     } 
23859                    e.setValue(sel.getAttribute(e.attrname));
23860                 });
23861             }
23862             
23863             var hasStyles = false;
23864             for(var i in this.styles) {
23865                 hasStyles = true;
23866                 break;
23867             }
23868             
23869             // update styles
23870             if (hasStyles) { 
23871                 var st = this.tb.fields.item(0);
23872                 
23873                 st.store.removeAll();
23874                
23875                 
23876                 var cn = sel.className.split(/\s+/);
23877                 
23878                 var avs = [];
23879                 if (this.styles['*']) {
23880                     
23881                     Roo.each(this.styles['*'], function(v) {
23882                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23883                     });
23884                 }
23885                 if (this.styles[tn]) { 
23886                     Roo.each(this.styles[tn], function(v) {
23887                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23888                     });
23889                 }
23890                 
23891                 st.store.loadData(avs);
23892                 st.collapse();
23893                 st.setValue(cn);
23894             }
23895             // flag our selected Node.
23896             this.tb.selectedNode = sel;
23897            
23898            
23899             Roo.menu.MenuMgr.hideAll();
23900
23901         }
23902         
23903         if (!updateFooter) {
23904             //this.footDisp.dom.innerHTML = ''; 
23905             return;
23906         }
23907         // update the footer
23908         //
23909         var html = '';
23910         
23911         this.footerEls = ans.reverse();
23912         Roo.each(this.footerEls, function(a,i) {
23913             if (!a) { return; }
23914             html += html.length ? ' &gt; '  :  '';
23915             
23916             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23917             
23918         });
23919        
23920         // 
23921         var sz = this.footDisp.up('td').getSize();
23922         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23923         this.footDisp.dom.style.marginLeft = '5px';
23924         
23925         this.footDisp.dom.style.overflow = 'hidden';
23926         
23927         this.footDisp.dom.innerHTML = html;
23928             
23929         //this.editorsyncValue();
23930     },
23931      
23932     
23933    
23934        
23935     // private
23936     onDestroy : function(){
23937         if(this.rendered){
23938             
23939             this.tb.items.each(function(item){
23940                 if(item.menu){
23941                     item.menu.removeAll();
23942                     if(item.menu.el){
23943                         item.menu.el.destroy();
23944                     }
23945                 }
23946                 item.destroy();
23947             });
23948              
23949         }
23950     },
23951     onFirstFocus: function() {
23952         // need to do this for all the toolbars..
23953         this.tb.items.each(function(item){
23954            item.enable();
23955         });
23956     },
23957     buildToolbar: function(tlist, nm)
23958     {
23959         var editor = this.editor;
23960         var editorcore = this.editorcore;
23961          // create a new element.
23962         var wdiv = editor.wrap.createChild({
23963                 tag: 'div'
23964             }, editor.wrap.dom.firstChild.nextSibling, true);
23965         
23966        
23967         var tb = new Roo.Toolbar(wdiv);
23968         // add the name..
23969         
23970         tb.add(nm+ ":&nbsp;");
23971         
23972         var styles = [];
23973         for(var i in this.styles) {
23974             styles.push(i);
23975         }
23976         
23977         // styles...
23978         if (styles && styles.length) {
23979             
23980             // this needs a multi-select checkbox...
23981             tb.addField( new Roo.form.ComboBox({
23982                 store: new Roo.data.SimpleStore({
23983                     id : 'val',
23984                     fields: ['val', 'selected'],
23985                     data : [] 
23986                 }),
23987                 name : '-roo-edit-className',
23988                 attrname : 'className',
23989                 displayField: 'val',
23990                 typeAhead: false,
23991                 mode: 'local',
23992                 editable : false,
23993                 triggerAction: 'all',
23994                 emptyText:'Select Style',
23995                 selectOnFocus:true,
23996                 width: 130,
23997                 listeners : {
23998                     'select': function(c, r, i) {
23999                         // initial support only for on class per el..
24000                         tb.selectedNode.className =  r ? r.get('val') : '';
24001                         editorcore.syncValue();
24002                     }
24003                 }
24004     
24005             }));
24006         }
24007         
24008         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24009         var tbops = tbc.options;
24010         
24011         for (var i in tlist) {
24012             
24013             var item = tlist[i];
24014             tb.add(item.title + ":&nbsp;");
24015             
24016             
24017             //optname == used so you can configure the options available..
24018             var opts = item.opts ? item.opts : false;
24019             if (item.optname) {
24020                 opts = tbops[item.optname];
24021            
24022             }
24023             
24024             if (opts) {
24025                 // opts == pulldown..
24026                 tb.addField( new Roo.form.ComboBox({
24027                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24028                         id : 'val',
24029                         fields: ['val', 'display'],
24030                         data : opts  
24031                     }),
24032                     name : '-roo-edit-' + i,
24033                     attrname : i,
24034                     stylename : item.style ? item.style : false,
24035                     displayField: item.displayField ? item.displayField : 'val',
24036                     valueField :  'val',
24037                     typeAhead: false,
24038                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24039                     editable : false,
24040                     triggerAction: 'all',
24041                     emptyText:'Select',
24042                     selectOnFocus:true,
24043                     width: item.width ? item.width  : 130,
24044                     listeners : {
24045                         'select': function(c, r, i) {
24046                             if (c.stylename) {
24047                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24048                                 return;
24049                             }
24050                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24051                         }
24052                     }
24053
24054                 }));
24055                 continue;
24056                     
24057                  
24058                 
24059                 tb.addField( new Roo.form.TextField({
24060                     name: i,
24061                     width: 100,
24062                     //allowBlank:false,
24063                     value: ''
24064                 }));
24065                 continue;
24066             }
24067             tb.addField( new Roo.form.TextField({
24068                 name: '-roo-edit-' + i,
24069                 attrname : i,
24070                 
24071                 width: item.width,
24072                 //allowBlank:true,
24073                 value: '',
24074                 listeners: {
24075                     'change' : function(f, nv, ov) {
24076                         tb.selectedNode.setAttribute(f.attrname, nv);
24077                         editorcore.syncValue();
24078                     }
24079                 }
24080             }));
24081              
24082         }
24083         
24084         var _this = this;
24085         
24086         if(nm == 'BODY'){
24087             tb.addSeparator();
24088         
24089             tb.addButton( {
24090                 text: 'Stylesheets',
24091
24092                 listeners : {
24093                     click : function ()
24094                     {
24095                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24096                     }
24097                 }
24098             });
24099         }
24100         
24101         tb.addFill();
24102         tb.addButton( {
24103             text: 'Remove Tag',
24104     
24105             listeners : {
24106                 click : function ()
24107                 {
24108                     // remove
24109                     // undo does not work.
24110                      
24111                     var sn = tb.selectedNode;
24112                     
24113                     var pn = sn.parentNode;
24114                     
24115                     var stn =  sn.childNodes[0];
24116                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24117                     while (sn.childNodes.length) {
24118                         var node = sn.childNodes[0];
24119                         sn.removeChild(node);
24120                         //Roo.log(node);
24121                         pn.insertBefore(node, sn);
24122                         
24123                     }
24124                     pn.removeChild(sn);
24125                     var range = editorcore.createRange();
24126         
24127                     range.setStart(stn,0);
24128                     range.setEnd(en,0); //????
24129                     //range.selectNode(sel);
24130                     
24131                     
24132                     var selection = editorcore.getSelection();
24133                     selection.removeAllRanges();
24134                     selection.addRange(range);
24135                     
24136                     
24137                     
24138                     //_this.updateToolbar(null, null, pn);
24139                     _this.updateToolbar(null, null, null);
24140                     _this.footDisp.dom.innerHTML = ''; 
24141                 }
24142             }
24143             
24144                     
24145                 
24146             
24147         });
24148         
24149         
24150         tb.el.on('click', function(e){
24151             e.preventDefault(); // what does this do?
24152         });
24153         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24154         tb.el.hide();
24155         tb.name = nm;
24156         // dont need to disable them... as they will get hidden
24157         return tb;
24158          
24159         
24160     },
24161     buildFooter : function()
24162     {
24163         
24164         var fel = this.editor.wrap.createChild();
24165         this.footer = new Roo.Toolbar(fel);
24166         // toolbar has scrolly on left / right?
24167         var footDisp= new Roo.Toolbar.Fill();
24168         var _t = this;
24169         this.footer.add(
24170             {
24171                 text : '&lt;',
24172                 xtype: 'Button',
24173                 handler : function() {
24174                     _t.footDisp.scrollTo('left',0,true)
24175                 }
24176             }
24177         );
24178         this.footer.add( footDisp );
24179         this.footer.add( 
24180             {
24181                 text : '&gt;',
24182                 xtype: 'Button',
24183                 handler : function() {
24184                     // no animation..
24185                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24186                 }
24187             }
24188         );
24189         var fel = Roo.get(footDisp.el);
24190         fel.addClass('x-editor-context');
24191         this.footDispWrap = fel; 
24192         this.footDispWrap.overflow  = 'hidden';
24193         
24194         this.footDisp = fel.createChild();
24195         this.footDispWrap.on('click', this.onContextClick, this)
24196         
24197         
24198     },
24199     onContextClick : function (ev,dom)
24200     {
24201         ev.preventDefault();
24202         var  cn = dom.className;
24203         //Roo.log(cn);
24204         if (!cn.match(/x-ed-loc-/)) {
24205             return;
24206         }
24207         var n = cn.split('-').pop();
24208         var ans = this.footerEls;
24209         var sel = ans[n];
24210         
24211          // pick
24212         var range = this.editorcore.createRange();
24213         
24214         range.selectNodeContents(sel);
24215         //range.selectNode(sel);
24216         
24217         
24218         var selection = this.editorcore.getSelection();
24219         selection.removeAllRanges();
24220         selection.addRange(range);
24221         
24222         
24223         
24224         this.updateToolbar(null, null, sel);
24225         
24226         
24227     }
24228     
24229     
24230     
24231     
24232     
24233 });
24234
24235
24236
24237
24238
24239 /*
24240  * Based on:
24241  * Ext JS Library 1.1.1
24242  * Copyright(c) 2006-2007, Ext JS, LLC.
24243  *
24244  * Originally Released Under LGPL - original licence link has changed is not relivant.
24245  *
24246  * Fork - LGPL
24247  * <script type="text/javascript">
24248  */
24249  
24250 /**
24251  * @class Roo.form.BasicForm
24252  * @extends Roo.util.Observable
24253  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24254  * @constructor
24255  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24256  * @param {Object} config Configuration options
24257  */
24258 Roo.form.BasicForm = function(el, config){
24259     this.allItems = [];
24260     this.childForms = [];
24261     Roo.apply(this, config);
24262     /*
24263      * The Roo.form.Field items in this form.
24264      * @type MixedCollection
24265      */
24266      
24267      
24268     this.items = new Roo.util.MixedCollection(false, function(o){
24269         return o.id || (o.id = Roo.id());
24270     });
24271     this.addEvents({
24272         /**
24273          * @event beforeaction
24274          * Fires before any action is performed. Return false to cancel the action.
24275          * @param {Form} this
24276          * @param {Action} action The action to be performed
24277          */
24278         beforeaction: true,
24279         /**
24280          * @event actionfailed
24281          * Fires when an action fails.
24282          * @param {Form} this
24283          * @param {Action} action The action that failed
24284          */
24285         actionfailed : true,
24286         /**
24287          * @event actioncomplete
24288          * Fires when an action is completed.
24289          * @param {Form} this
24290          * @param {Action} action The action that completed
24291          */
24292         actioncomplete : true
24293     });
24294     if(el){
24295         this.initEl(el);
24296     }
24297     Roo.form.BasicForm.superclass.constructor.call(this);
24298 };
24299
24300 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24301     /**
24302      * @cfg {String} method
24303      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24304      */
24305     /**
24306      * @cfg {DataReader} reader
24307      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24308      * This is optional as there is built-in support for processing JSON.
24309      */
24310     /**
24311      * @cfg {DataReader} errorReader
24312      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24313      * This is completely optional as there is built-in support for processing JSON.
24314      */
24315     /**
24316      * @cfg {String} url
24317      * The URL to use for form actions if one isn't supplied in the action options.
24318      */
24319     /**
24320      * @cfg {Boolean} fileUpload
24321      * Set to true if this form is a file upload.
24322      */
24323      
24324     /**
24325      * @cfg {Object} baseParams
24326      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24327      */
24328      /**
24329      
24330     /**
24331      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24332      */
24333     timeout: 30,
24334
24335     // private
24336     activeAction : null,
24337
24338     /**
24339      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24340      * or setValues() data instead of when the form was first created.
24341      */
24342     trackResetOnLoad : false,
24343     
24344     
24345     /**
24346      * childForms - used for multi-tab forms
24347      * @type {Array}
24348      */
24349     childForms : false,
24350     
24351     /**
24352      * allItems - full list of fields.
24353      * @type {Array}
24354      */
24355     allItems : false,
24356     
24357     /**
24358      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24359      * element by passing it or its id or mask the form itself by passing in true.
24360      * @type Mixed
24361      */
24362     waitMsgTarget : false,
24363     
24364     /**
24365      * @type Boolean
24366      */
24367     disableMask : false,
24368
24369     // private
24370     initEl : function(el){
24371         this.el = Roo.get(el);
24372         this.id = this.el.id || Roo.id();
24373         this.el.on('submit', this.onSubmit, this);
24374         this.el.addClass('x-form');
24375     },
24376
24377     // private
24378     onSubmit : function(e){
24379         e.stopEvent();
24380     },
24381
24382     /**
24383      * Returns true if client-side validation on the form is successful.
24384      * @return Boolean
24385      */
24386     isValid : function(){
24387         var valid = true;
24388         this.items.each(function(f){
24389            if(!f.validate()){
24390                valid = false;
24391            }
24392         });
24393         return valid;
24394     },
24395
24396     /**
24397      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24398      * @return Boolean
24399      */
24400     isDirty : function(){
24401         var dirty = false;
24402         this.items.each(function(f){
24403            if(f.isDirty()){
24404                dirty = true;
24405                return false;
24406            }
24407         });
24408         return dirty;
24409     },
24410     
24411     /**
24412      * Returns true if any fields in this form have changed since their original load. (New version)
24413      * @return Boolean
24414      */
24415     
24416     hasChanged : function()
24417     {
24418         var dirty = false;
24419         this.items.each(function(f){
24420            if(f.hasChanged()){
24421                dirty = true;
24422                return false;
24423            }
24424         });
24425         return dirty;
24426         
24427     },
24428     /**
24429      * Resets all hasChanged to 'false' -
24430      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24431      * So hasChanged storage is only to be used for this purpose
24432      * @return Boolean
24433      */
24434     resetHasChanged : function()
24435     {
24436         this.items.each(function(f){
24437            f.resetHasChanged();
24438         });
24439         
24440     },
24441     
24442     
24443     /**
24444      * Performs a predefined action (submit or load) or custom actions you define on this form.
24445      * @param {String} actionName The name of the action type
24446      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24447      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24448      * accept other config options):
24449      * <pre>
24450 Property          Type             Description
24451 ----------------  ---------------  ----------------------------------------------------------------------------------
24452 url               String           The url for the action (defaults to the form's url)
24453 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24454 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24455 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24456                                    validate the form on the client (defaults to false)
24457      * </pre>
24458      * @return {BasicForm} this
24459      */
24460     doAction : function(action, options){
24461         if(typeof action == 'string'){
24462             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24463         }
24464         if(this.fireEvent('beforeaction', this, action) !== false){
24465             this.beforeAction(action);
24466             action.run.defer(100, action);
24467         }
24468         return this;
24469     },
24470
24471     /**
24472      * Shortcut to do a submit action.
24473      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24474      * @return {BasicForm} this
24475      */
24476     submit : function(options){
24477         this.doAction('submit', options);
24478         return this;
24479     },
24480
24481     /**
24482      * Shortcut to do a load action.
24483      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24484      * @return {BasicForm} this
24485      */
24486     load : function(options){
24487         this.doAction('load', options);
24488         return this;
24489     },
24490
24491     /**
24492      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24493      * @param {Record} record The record to edit
24494      * @return {BasicForm} this
24495      */
24496     updateRecord : function(record){
24497         record.beginEdit();
24498         var fs = record.fields;
24499         fs.each(function(f){
24500             var field = this.findField(f.name);
24501             if(field){
24502                 record.set(f.name, field.getValue());
24503             }
24504         }, this);
24505         record.endEdit();
24506         return this;
24507     },
24508
24509     /**
24510      * Loads an Roo.data.Record into this form.
24511      * @param {Record} record The record to load
24512      * @return {BasicForm} this
24513      */
24514     loadRecord : function(record){
24515         this.setValues(record.data);
24516         return this;
24517     },
24518
24519     // private
24520     beforeAction : function(action){
24521         var o = action.options;
24522         
24523         if(!this.disableMask) {
24524             if(this.waitMsgTarget === true){
24525                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24526             }else if(this.waitMsgTarget){
24527                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24528                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24529             }else {
24530                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24531             }
24532         }
24533         
24534          
24535     },
24536
24537     // private
24538     afterAction : function(action, success){
24539         this.activeAction = null;
24540         var o = action.options;
24541         
24542         if(!this.disableMask) {
24543             if(this.waitMsgTarget === true){
24544                 this.el.unmask();
24545             }else if(this.waitMsgTarget){
24546                 this.waitMsgTarget.unmask();
24547             }else{
24548                 Roo.MessageBox.updateProgress(1);
24549                 Roo.MessageBox.hide();
24550             }
24551         }
24552         
24553         if(success){
24554             if(o.reset){
24555                 this.reset();
24556             }
24557             Roo.callback(o.success, o.scope, [this, action]);
24558             this.fireEvent('actioncomplete', this, action);
24559             
24560         }else{
24561             
24562             // failure condition..
24563             // we have a scenario where updates need confirming.
24564             // eg. if a locking scenario exists..
24565             // we look for { errors : { needs_confirm : true }} in the response.
24566             if (
24567                 (typeof(action.result) != 'undefined')  &&
24568                 (typeof(action.result.errors) != 'undefined')  &&
24569                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24570            ){
24571                 var _t = this;
24572                 Roo.MessageBox.confirm(
24573                     "Change requires confirmation",
24574                     action.result.errorMsg,
24575                     function(r) {
24576                         if (r != 'yes') {
24577                             return;
24578                         }
24579                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24580                     }
24581                     
24582                 );
24583                 
24584                 
24585                 
24586                 return;
24587             }
24588             
24589             Roo.callback(o.failure, o.scope, [this, action]);
24590             // show an error message if no failed handler is set..
24591             if (!this.hasListener('actionfailed')) {
24592                 Roo.MessageBox.alert("Error",
24593                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24594                         action.result.errorMsg :
24595                         "Saving Failed, please check your entries or try again"
24596                 );
24597             }
24598             
24599             this.fireEvent('actionfailed', this, action);
24600         }
24601         
24602     },
24603
24604     /**
24605      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24606      * @param {String} id The value to search for
24607      * @return Field
24608      */
24609     findField : function(id){
24610         var field = this.items.get(id);
24611         if(!field){
24612             this.items.each(function(f){
24613                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24614                     field = f;
24615                     return false;
24616                 }
24617             });
24618         }
24619         return field || null;
24620     },
24621
24622     /**
24623      * Add a secondary form to this one, 
24624      * Used to provide tabbed forms. One form is primary, with hidden values 
24625      * which mirror the elements from the other forms.
24626      * 
24627      * @param {Roo.form.Form} form to add.
24628      * 
24629      */
24630     addForm : function(form)
24631     {
24632        
24633         if (this.childForms.indexOf(form) > -1) {
24634             // already added..
24635             return;
24636         }
24637         this.childForms.push(form);
24638         var n = '';
24639         Roo.each(form.allItems, function (fe) {
24640             
24641             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24642             if (this.findField(n)) { // already added..
24643                 return;
24644             }
24645             var add = new Roo.form.Hidden({
24646                 name : n
24647             });
24648             add.render(this.el);
24649             
24650             this.add( add );
24651         }, this);
24652         
24653     },
24654     /**
24655      * Mark fields in this form invalid in bulk.
24656      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24657      * @return {BasicForm} this
24658      */
24659     markInvalid : function(errors){
24660         if(errors instanceof Array){
24661             for(var i = 0, len = errors.length; i < len; i++){
24662                 var fieldError = errors[i];
24663                 var f = this.findField(fieldError.id);
24664                 if(f){
24665                     f.markInvalid(fieldError.msg);
24666                 }
24667             }
24668         }else{
24669             var field, id;
24670             for(id in errors){
24671                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24672                     field.markInvalid(errors[id]);
24673                 }
24674             }
24675         }
24676         Roo.each(this.childForms || [], function (f) {
24677             f.markInvalid(errors);
24678         });
24679         
24680         return this;
24681     },
24682
24683     /**
24684      * Set values for fields in this form in bulk.
24685      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24686      * @return {BasicForm} this
24687      */
24688     setValues : function(values){
24689         if(values instanceof Array){ // array of objects
24690             for(var i = 0, len = values.length; i < len; i++){
24691                 var v = values[i];
24692                 var f = this.findField(v.id);
24693                 if(f){
24694                     f.setValue(v.value);
24695                     if(this.trackResetOnLoad){
24696                         f.originalValue = f.getValue();
24697                     }
24698                 }
24699             }
24700         }else{ // object hash
24701             var field, id;
24702             for(id in values){
24703                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24704                     
24705                     if (field.setFromData && 
24706                         field.valueField && 
24707                         field.displayField &&
24708                         // combos' with local stores can 
24709                         // be queried via setValue()
24710                         // to set their value..
24711                         (field.store && !field.store.isLocal)
24712                         ) {
24713                         // it's a combo
24714                         var sd = { };
24715                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24716                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24717                         field.setFromData(sd);
24718                         
24719                     } else {
24720                         field.setValue(values[id]);
24721                     }
24722                     
24723                     
24724                     if(this.trackResetOnLoad){
24725                         field.originalValue = field.getValue();
24726                     }
24727                 }
24728             }
24729         }
24730         this.resetHasChanged();
24731         
24732         
24733         Roo.each(this.childForms || [], function (f) {
24734             f.setValues(values);
24735             f.resetHasChanged();
24736         });
24737                 
24738         return this;
24739     },
24740
24741     /**
24742      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24743      * they are returned as an array.
24744      * @param {Boolean} asString
24745      * @return {Object}
24746      */
24747     getValues : function(asString){
24748         if (this.childForms) {
24749             // copy values from the child forms
24750             Roo.each(this.childForms, function (f) {
24751                 this.setValues(f.getValues());
24752             }, this);
24753         }
24754         
24755         
24756         
24757         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24758         if(asString === true){
24759             return fs;
24760         }
24761         return Roo.urlDecode(fs);
24762     },
24763     
24764     /**
24765      * Returns the fields in this form as an object with key/value pairs. 
24766      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24767      * @return {Object}
24768      */
24769     getFieldValues : function(with_hidden)
24770     {
24771         if (this.childForms) {
24772             // copy values from the child forms
24773             // should this call getFieldValues - probably not as we do not currently copy
24774             // hidden fields when we generate..
24775             Roo.each(this.childForms, function (f) {
24776                 this.setValues(f.getValues());
24777             }, this);
24778         }
24779         
24780         var ret = {};
24781         this.items.each(function(f){
24782             if (!f.getName()) {
24783                 return;
24784             }
24785             var v = f.getValue();
24786             if (f.inputType =='radio') {
24787                 if (typeof(ret[f.getName()]) == 'undefined') {
24788                     ret[f.getName()] = ''; // empty..
24789                 }
24790                 
24791                 if (!f.el.dom.checked) {
24792                     return;
24793                     
24794                 }
24795                 v = f.el.dom.value;
24796                 
24797             }
24798             
24799             // not sure if this supported any more..
24800             if ((typeof(v) == 'object') && f.getRawValue) {
24801                 v = f.getRawValue() ; // dates..
24802             }
24803             // combo boxes where name != hiddenName...
24804             if (f.name != f.getName()) {
24805                 ret[f.name] = f.getRawValue();
24806             }
24807             ret[f.getName()] = v;
24808         });
24809         
24810         return ret;
24811     },
24812
24813     /**
24814      * Clears all invalid messages in this form.
24815      * @return {BasicForm} this
24816      */
24817     clearInvalid : function(){
24818         this.items.each(function(f){
24819            f.clearInvalid();
24820         });
24821         
24822         Roo.each(this.childForms || [], function (f) {
24823             f.clearInvalid();
24824         });
24825         
24826         
24827         return this;
24828     },
24829
24830     /**
24831      * Resets this form.
24832      * @return {BasicForm} this
24833      */
24834     reset : function(){
24835         this.items.each(function(f){
24836             f.reset();
24837         });
24838         
24839         Roo.each(this.childForms || [], function (f) {
24840             f.reset();
24841         });
24842         this.resetHasChanged();
24843         
24844         return this;
24845     },
24846
24847     /**
24848      * Add Roo.form components to this form.
24849      * @param {Field} field1
24850      * @param {Field} field2 (optional)
24851      * @param {Field} etc (optional)
24852      * @return {BasicForm} this
24853      */
24854     add : function(){
24855         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24856         return this;
24857     },
24858
24859
24860     /**
24861      * Removes a field from the items collection (does NOT remove its markup).
24862      * @param {Field} field
24863      * @return {BasicForm} this
24864      */
24865     remove : function(field){
24866         this.items.remove(field);
24867         return this;
24868     },
24869
24870     /**
24871      * Looks at the fields in this form, checks them for an id attribute,
24872      * and calls applyTo on the existing dom element with that id.
24873      * @return {BasicForm} this
24874      */
24875     render : function(){
24876         this.items.each(function(f){
24877             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24878                 f.applyTo(f.id);
24879             }
24880         });
24881         return this;
24882     },
24883
24884     /**
24885      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24886      * @param {Object} values
24887      * @return {BasicForm} this
24888      */
24889     applyToFields : function(o){
24890         this.items.each(function(f){
24891            Roo.apply(f, o);
24892         });
24893         return this;
24894     },
24895
24896     /**
24897      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24898      * @param {Object} values
24899      * @return {BasicForm} this
24900      */
24901     applyIfToFields : function(o){
24902         this.items.each(function(f){
24903            Roo.applyIf(f, o);
24904         });
24905         return this;
24906     }
24907 });
24908
24909 // back compat
24910 Roo.BasicForm = Roo.form.BasicForm;/*
24911  * Based on:
24912  * Ext JS Library 1.1.1
24913  * Copyright(c) 2006-2007, Ext JS, LLC.
24914  *
24915  * Originally Released Under LGPL - original licence link has changed is not relivant.
24916  *
24917  * Fork - LGPL
24918  * <script type="text/javascript">
24919  */
24920
24921 /**
24922  * @class Roo.form.Form
24923  * @extends Roo.form.BasicForm
24924  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24925  * @constructor
24926  * @param {Object} config Configuration options
24927  */
24928 Roo.form.Form = function(config){
24929     var xitems =  [];
24930     if (config.items) {
24931         xitems = config.items;
24932         delete config.items;
24933     }
24934    
24935     
24936     Roo.form.Form.superclass.constructor.call(this, null, config);
24937     this.url = this.url || this.action;
24938     if(!this.root){
24939         this.root = new Roo.form.Layout(Roo.applyIf({
24940             id: Roo.id()
24941         }, config));
24942     }
24943     this.active = this.root;
24944     /**
24945      * Array of all the buttons that have been added to this form via {@link addButton}
24946      * @type Array
24947      */
24948     this.buttons = [];
24949     this.allItems = [];
24950     this.addEvents({
24951         /**
24952          * @event clientvalidation
24953          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24954          * @param {Form} this
24955          * @param {Boolean} valid true if the form has passed client-side validation
24956          */
24957         clientvalidation: true,
24958         /**
24959          * @event rendered
24960          * Fires when the form is rendered
24961          * @param {Roo.form.Form} form
24962          */
24963         rendered : true
24964     });
24965     
24966     if (this.progressUrl) {
24967             // push a hidden field onto the list of fields..
24968             this.addxtype( {
24969                     xns: Roo.form, 
24970                     xtype : 'Hidden', 
24971                     name : 'UPLOAD_IDENTIFIER' 
24972             });
24973         }
24974         
24975     
24976     Roo.each(xitems, this.addxtype, this);
24977     
24978     
24979     
24980 };
24981
24982 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24983     /**
24984      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24985      */
24986     /**
24987      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24988      */
24989     /**
24990      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24991      */
24992     buttonAlign:'center',
24993
24994     /**
24995      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24996      */
24997     minButtonWidth:75,
24998
24999     /**
25000      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25001      * This property cascades to child containers if not set.
25002      */
25003     labelAlign:'left',
25004
25005     /**
25006      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25007      * fires a looping event with that state. This is required to bind buttons to the valid
25008      * state using the config value formBind:true on the button.
25009      */
25010     monitorValid : false,
25011
25012     /**
25013      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25014      */
25015     monitorPoll : 200,
25016     
25017     /**
25018      * @cfg {String} progressUrl - Url to return progress data 
25019      */
25020     
25021     progressUrl : false,
25022     /**
25023      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25024      * sending a formdata with extra parameters - eg uploaded elements.
25025      */
25026     
25027     formData : false,
25028     
25029     /**
25030      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25031      * fields are added and the column is closed. If no fields are passed the column remains open
25032      * until end() is called.
25033      * @param {Object} config The config to pass to the column
25034      * @param {Field} field1 (optional)
25035      * @param {Field} field2 (optional)
25036      * @param {Field} etc (optional)
25037      * @return Column The column container object
25038      */
25039     column : function(c){
25040         var col = new Roo.form.Column(c);
25041         this.start(col);
25042         if(arguments.length > 1){ // duplicate code required because of Opera
25043             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25044             this.end();
25045         }
25046         return col;
25047     },
25048
25049     /**
25050      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25051      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25052      * until end() is called.
25053      * @param {Object} config The config to pass to the fieldset
25054      * @param {Field} field1 (optional)
25055      * @param {Field} field2 (optional)
25056      * @param {Field} etc (optional)
25057      * @return FieldSet The fieldset container object
25058      */
25059     fieldset : function(c){
25060         var fs = new Roo.form.FieldSet(c);
25061         this.start(fs);
25062         if(arguments.length > 1){ // duplicate code required because of Opera
25063             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25064             this.end();
25065         }
25066         return fs;
25067     },
25068
25069     /**
25070      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25071      * fields are added and the container is closed. If no fields are passed the container remains open
25072      * until end() is called.
25073      * @param {Object} config The config to pass to the Layout
25074      * @param {Field} field1 (optional)
25075      * @param {Field} field2 (optional)
25076      * @param {Field} etc (optional)
25077      * @return Layout The container object
25078      */
25079     container : function(c){
25080         var l = new Roo.form.Layout(c);
25081         this.start(l);
25082         if(arguments.length > 1){ // duplicate code required because of Opera
25083             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25084             this.end();
25085         }
25086         return l;
25087     },
25088
25089     /**
25090      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25091      * @param {Object} container A Roo.form.Layout or subclass of Layout
25092      * @return {Form} this
25093      */
25094     start : function(c){
25095         // cascade label info
25096         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25097         this.active.stack.push(c);
25098         c.ownerCt = this.active;
25099         this.active = c;
25100         return this;
25101     },
25102
25103     /**
25104      * Closes the current open container
25105      * @return {Form} this
25106      */
25107     end : function(){
25108         if(this.active == this.root){
25109             return this;
25110         }
25111         this.active = this.active.ownerCt;
25112         return this;
25113     },
25114
25115     /**
25116      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25117      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25118      * as the label of the field.
25119      * @param {Field} field1
25120      * @param {Field} field2 (optional)
25121      * @param {Field} etc. (optional)
25122      * @return {Form} this
25123      */
25124     add : function(){
25125         this.active.stack.push.apply(this.active.stack, arguments);
25126         this.allItems.push.apply(this.allItems,arguments);
25127         var r = [];
25128         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25129             if(a[i].isFormField){
25130                 r.push(a[i]);
25131             }
25132         }
25133         if(r.length > 0){
25134             Roo.form.Form.superclass.add.apply(this, r);
25135         }
25136         return this;
25137     },
25138     
25139
25140     
25141     
25142     
25143      /**
25144      * Find any element that has been added to a form, using it's ID or name
25145      * This can include framesets, columns etc. along with regular fields..
25146      * @param {String} id - id or name to find.
25147      
25148      * @return {Element} e - or false if nothing found.
25149      */
25150     findbyId : function(id)
25151     {
25152         var ret = false;
25153         if (!id) {
25154             return ret;
25155         }
25156         Roo.each(this.allItems, function(f){
25157             if (f.id == id || f.name == id ){
25158                 ret = f;
25159                 return false;
25160             }
25161         });
25162         return ret;
25163     },
25164
25165     
25166     
25167     /**
25168      * Render this form into the passed container. This should only be called once!
25169      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25170      * @return {Form} this
25171      */
25172     render : function(ct)
25173     {
25174         
25175         
25176         
25177         ct = Roo.get(ct);
25178         var o = this.autoCreate || {
25179             tag: 'form',
25180             method : this.method || 'POST',
25181             id : this.id || Roo.id()
25182         };
25183         this.initEl(ct.createChild(o));
25184
25185         this.root.render(this.el);
25186         
25187        
25188              
25189         this.items.each(function(f){
25190             f.render('x-form-el-'+f.id);
25191         });
25192
25193         if(this.buttons.length > 0){
25194             // tables are required to maintain order and for correct IE layout
25195             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25196                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25197                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25198             }}, null, true);
25199             var tr = tb.getElementsByTagName('tr')[0];
25200             for(var i = 0, len = this.buttons.length; i < len; i++) {
25201                 var b = this.buttons[i];
25202                 var td = document.createElement('td');
25203                 td.className = 'x-form-btn-td';
25204                 b.render(tr.appendChild(td));
25205             }
25206         }
25207         if(this.monitorValid){ // initialize after render
25208             this.startMonitoring();
25209         }
25210         this.fireEvent('rendered', this);
25211         return this;
25212     },
25213
25214     /**
25215      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25216      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25217      * object or a valid Roo.DomHelper element config
25218      * @param {Function} handler The function called when the button is clicked
25219      * @param {Object} scope (optional) The scope of the handler function
25220      * @return {Roo.Button}
25221      */
25222     addButton : function(config, handler, scope){
25223         var bc = {
25224             handler: handler,
25225             scope: scope,
25226             minWidth: this.minButtonWidth,
25227             hideParent:true
25228         };
25229         if(typeof config == "string"){
25230             bc.text = config;
25231         }else{
25232             Roo.apply(bc, config);
25233         }
25234         var btn = new Roo.Button(null, bc);
25235         this.buttons.push(btn);
25236         return btn;
25237     },
25238
25239      /**
25240      * Adds a series of form elements (using the xtype property as the factory method.
25241      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25242      * @param {Object} config 
25243      */
25244     
25245     addxtype : function()
25246     {
25247         var ar = Array.prototype.slice.call(arguments, 0);
25248         var ret = false;
25249         for(var i = 0; i < ar.length; i++) {
25250             if (!ar[i]) {
25251                 continue; // skip -- if this happends something invalid got sent, we 
25252                 // should ignore it, as basically that interface element will not show up
25253                 // and that should be pretty obvious!!
25254             }
25255             
25256             if (Roo.form[ar[i].xtype]) {
25257                 ar[i].form = this;
25258                 var fe = Roo.factory(ar[i], Roo.form);
25259                 if (!ret) {
25260                     ret = fe;
25261                 }
25262                 fe.form = this;
25263                 if (fe.store) {
25264                     fe.store.form = this;
25265                 }
25266                 if (fe.isLayout) {  
25267                          
25268                     this.start(fe);
25269                     this.allItems.push(fe);
25270                     if (fe.items && fe.addxtype) {
25271                         fe.addxtype.apply(fe, fe.items);
25272                         delete fe.items;
25273                     }
25274                      this.end();
25275                     continue;
25276                 }
25277                 
25278                 
25279                  
25280                 this.add(fe);
25281               //  console.log('adding ' + ar[i].xtype);
25282             }
25283             if (ar[i].xtype == 'Button') {  
25284                 //console.log('adding button');
25285                 //console.log(ar[i]);
25286                 this.addButton(ar[i]);
25287                 this.allItems.push(fe);
25288                 continue;
25289             }
25290             
25291             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25292                 alert('end is not supported on xtype any more, use items');
25293             //    this.end();
25294             //    //console.log('adding end');
25295             }
25296             
25297         }
25298         return ret;
25299     },
25300     
25301     /**
25302      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25303      * option "monitorValid"
25304      */
25305     startMonitoring : function(){
25306         if(!this.bound){
25307             this.bound = true;
25308             Roo.TaskMgr.start({
25309                 run : this.bindHandler,
25310                 interval : this.monitorPoll || 200,
25311                 scope: this
25312             });
25313         }
25314     },
25315
25316     /**
25317      * Stops monitoring of the valid state of this form
25318      */
25319     stopMonitoring : function(){
25320         this.bound = false;
25321     },
25322
25323     // private
25324     bindHandler : function(){
25325         if(!this.bound){
25326             return false; // stops binding
25327         }
25328         var valid = true;
25329         this.items.each(function(f){
25330             if(!f.isValid(true)){
25331                 valid = false;
25332                 return false;
25333             }
25334         });
25335         for(var i = 0, len = this.buttons.length; i < len; i++){
25336             var btn = this.buttons[i];
25337             if(btn.formBind === true && btn.disabled === valid){
25338                 btn.setDisabled(!valid);
25339             }
25340         }
25341         this.fireEvent('clientvalidation', this, valid);
25342     }
25343     
25344     
25345     
25346     
25347     
25348     
25349     
25350     
25351 });
25352
25353
25354 // back compat
25355 Roo.Form = Roo.form.Form;
25356 /*
25357  * Based on:
25358  * Ext JS Library 1.1.1
25359  * Copyright(c) 2006-2007, Ext JS, LLC.
25360  *
25361  * Originally Released Under LGPL - original licence link has changed is not relivant.
25362  *
25363  * Fork - LGPL
25364  * <script type="text/javascript">
25365  */
25366
25367 // as we use this in bootstrap.
25368 Roo.namespace('Roo.form');
25369  /**
25370  * @class Roo.form.Action
25371  * Internal Class used to handle form actions
25372  * @constructor
25373  * @param {Roo.form.BasicForm} el The form element or its id
25374  * @param {Object} config Configuration options
25375  */
25376
25377  
25378  
25379 // define the action interface
25380 Roo.form.Action = function(form, options){
25381     this.form = form;
25382     this.options = options || {};
25383 };
25384 /**
25385  * Client Validation Failed
25386  * @const 
25387  */
25388 Roo.form.Action.CLIENT_INVALID = 'client';
25389 /**
25390  * Server Validation Failed
25391  * @const 
25392  */
25393 Roo.form.Action.SERVER_INVALID = 'server';
25394  /**
25395  * Connect to Server Failed
25396  * @const 
25397  */
25398 Roo.form.Action.CONNECT_FAILURE = 'connect';
25399 /**
25400  * Reading Data from Server Failed
25401  * @const 
25402  */
25403 Roo.form.Action.LOAD_FAILURE = 'load';
25404
25405 Roo.form.Action.prototype = {
25406     type : 'default',
25407     failureType : undefined,
25408     response : undefined,
25409     result : undefined,
25410
25411     // interface method
25412     run : function(options){
25413
25414     },
25415
25416     // interface method
25417     success : function(response){
25418
25419     },
25420
25421     // interface method
25422     handleResponse : function(response){
25423
25424     },
25425
25426     // default connection failure
25427     failure : function(response){
25428         
25429         this.response = response;
25430         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25431         this.form.afterAction(this, false);
25432     },
25433
25434     processResponse : function(response){
25435         this.response = response;
25436         if(!response.responseText){
25437             return true;
25438         }
25439         this.result = this.handleResponse(response);
25440         return this.result;
25441     },
25442
25443     // utility functions used internally
25444     getUrl : function(appendParams){
25445         var url = this.options.url || this.form.url || this.form.el.dom.action;
25446         if(appendParams){
25447             var p = this.getParams();
25448             if(p){
25449                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25450             }
25451         }
25452         return url;
25453     },
25454
25455     getMethod : function(){
25456         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25457     },
25458
25459     getParams : function(){
25460         var bp = this.form.baseParams;
25461         var p = this.options.params;
25462         if(p){
25463             if(typeof p == "object"){
25464                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25465             }else if(typeof p == 'string' && bp){
25466                 p += '&' + Roo.urlEncode(bp);
25467             }
25468         }else if(bp){
25469             p = Roo.urlEncode(bp);
25470         }
25471         return p;
25472     },
25473
25474     createCallback : function(){
25475         return {
25476             success: this.success,
25477             failure: this.failure,
25478             scope: this,
25479             timeout: (this.form.timeout*1000),
25480             upload: this.form.fileUpload ? this.success : undefined
25481         };
25482     }
25483 };
25484
25485 Roo.form.Action.Submit = function(form, options){
25486     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25487 };
25488
25489 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25490     type : 'submit',
25491
25492     haveProgress : false,
25493     uploadComplete : false,
25494     
25495     // uploadProgress indicator.
25496     uploadProgress : function()
25497     {
25498         if (!this.form.progressUrl) {
25499             return;
25500         }
25501         
25502         if (!this.haveProgress) {
25503             Roo.MessageBox.progress("Uploading", "Uploading");
25504         }
25505         if (this.uploadComplete) {
25506            Roo.MessageBox.hide();
25507            return;
25508         }
25509         
25510         this.haveProgress = true;
25511    
25512         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25513         
25514         var c = new Roo.data.Connection();
25515         c.request({
25516             url : this.form.progressUrl,
25517             params: {
25518                 id : uid
25519             },
25520             method: 'GET',
25521             success : function(req){
25522                //console.log(data);
25523                 var rdata = false;
25524                 var edata;
25525                 try  {
25526                    rdata = Roo.decode(req.responseText)
25527                 } catch (e) {
25528                     Roo.log("Invalid data from server..");
25529                     Roo.log(edata);
25530                     return;
25531                 }
25532                 if (!rdata || !rdata.success) {
25533                     Roo.log(rdata);
25534                     Roo.MessageBox.alert(Roo.encode(rdata));
25535                     return;
25536                 }
25537                 var data = rdata.data;
25538                 
25539                 if (this.uploadComplete) {
25540                    Roo.MessageBox.hide();
25541                    return;
25542                 }
25543                    
25544                 if (data){
25545                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25546                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25547                     );
25548                 }
25549                 this.uploadProgress.defer(2000,this);
25550             },
25551        
25552             failure: function(data) {
25553                 Roo.log('progress url failed ');
25554                 Roo.log(data);
25555             },
25556             scope : this
25557         });
25558            
25559     },
25560     
25561     
25562     run : function()
25563     {
25564         // run get Values on the form, so it syncs any secondary forms.
25565         this.form.getValues();
25566         
25567         var o = this.options;
25568         var method = this.getMethod();
25569         var isPost = method == 'POST';
25570         if(o.clientValidation === false || this.form.isValid()){
25571             
25572             if (this.form.progressUrl) {
25573                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25574                     (new Date() * 1) + '' + Math.random());
25575                     
25576             } 
25577             
25578             
25579             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25580                 form:this.form.el.dom,
25581                 url:this.getUrl(!isPost),
25582                 method: method,
25583                 params:isPost ? this.getParams() : null,
25584                 isUpload: this.form.fileUpload,
25585                 formData : this.form.formData
25586             }));
25587             
25588             this.uploadProgress();
25589
25590         }else if (o.clientValidation !== false){ // client validation failed
25591             this.failureType = Roo.form.Action.CLIENT_INVALID;
25592             this.form.afterAction(this, false);
25593         }
25594     },
25595
25596     success : function(response)
25597     {
25598         this.uploadComplete= true;
25599         if (this.haveProgress) {
25600             Roo.MessageBox.hide();
25601         }
25602         
25603         
25604         var result = this.processResponse(response);
25605         if(result === true || result.success){
25606             this.form.afterAction(this, true);
25607             return;
25608         }
25609         if(result.errors){
25610             this.form.markInvalid(result.errors);
25611             this.failureType = Roo.form.Action.SERVER_INVALID;
25612         }
25613         this.form.afterAction(this, false);
25614     },
25615     failure : function(response)
25616     {
25617         this.uploadComplete= true;
25618         if (this.haveProgress) {
25619             Roo.MessageBox.hide();
25620         }
25621         
25622         this.response = response;
25623         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25624         this.form.afterAction(this, false);
25625     },
25626     
25627     handleResponse : function(response){
25628         if(this.form.errorReader){
25629             var rs = this.form.errorReader.read(response);
25630             var errors = [];
25631             if(rs.records){
25632                 for(var i = 0, len = rs.records.length; i < len; i++) {
25633                     var r = rs.records[i];
25634                     errors[i] = r.data;
25635                 }
25636             }
25637             if(errors.length < 1){
25638                 errors = null;
25639             }
25640             return {
25641                 success : rs.success,
25642                 errors : errors
25643             };
25644         }
25645         var ret = false;
25646         try {
25647             ret = Roo.decode(response.responseText);
25648         } catch (e) {
25649             ret = {
25650                 success: false,
25651                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25652                 errors : []
25653             };
25654         }
25655         return ret;
25656         
25657     }
25658 });
25659
25660
25661 Roo.form.Action.Load = function(form, options){
25662     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25663     this.reader = this.form.reader;
25664 };
25665
25666 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25667     type : 'load',
25668
25669     run : function(){
25670         
25671         Roo.Ajax.request(Roo.apply(
25672                 this.createCallback(), {
25673                     method:this.getMethod(),
25674                     url:this.getUrl(false),
25675                     params:this.getParams()
25676         }));
25677     },
25678
25679     success : function(response){
25680         
25681         var result = this.processResponse(response);
25682         if(result === true || !result.success || !result.data){
25683             this.failureType = Roo.form.Action.LOAD_FAILURE;
25684             this.form.afterAction(this, false);
25685             return;
25686         }
25687         this.form.clearInvalid();
25688         this.form.setValues(result.data);
25689         this.form.afterAction(this, true);
25690     },
25691
25692     handleResponse : function(response){
25693         if(this.form.reader){
25694             var rs = this.form.reader.read(response);
25695             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25696             return {
25697                 success : rs.success,
25698                 data : data
25699             };
25700         }
25701         return Roo.decode(response.responseText);
25702     }
25703 });
25704
25705 Roo.form.Action.ACTION_TYPES = {
25706     'load' : Roo.form.Action.Load,
25707     'submit' : Roo.form.Action.Submit
25708 };/*
25709  * Based on:
25710  * Ext JS Library 1.1.1
25711  * Copyright(c) 2006-2007, Ext JS, LLC.
25712  *
25713  * Originally Released Under LGPL - original licence link has changed is not relivant.
25714  *
25715  * Fork - LGPL
25716  * <script type="text/javascript">
25717  */
25718  
25719 /**
25720  * @class Roo.form.Layout
25721  * @extends Roo.Component
25722  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25723  * @constructor
25724  * @param {Object} config Configuration options
25725  */
25726 Roo.form.Layout = function(config){
25727     var xitems = [];
25728     if (config.items) {
25729         xitems = config.items;
25730         delete config.items;
25731     }
25732     Roo.form.Layout.superclass.constructor.call(this, config);
25733     this.stack = [];
25734     Roo.each(xitems, this.addxtype, this);
25735      
25736 };
25737
25738 Roo.extend(Roo.form.Layout, Roo.Component, {
25739     /**
25740      * @cfg {String/Object} autoCreate
25741      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25742      */
25743     /**
25744      * @cfg {String/Object/Function} style
25745      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25746      * a function which returns such a specification.
25747      */
25748     /**
25749      * @cfg {String} labelAlign
25750      * Valid values are "left," "top" and "right" (defaults to "left")
25751      */
25752     /**
25753      * @cfg {Number} labelWidth
25754      * Fixed width in pixels of all field labels (defaults to undefined)
25755      */
25756     /**
25757      * @cfg {Boolean} clear
25758      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25759      */
25760     clear : true,
25761     /**
25762      * @cfg {String} labelSeparator
25763      * The separator to use after field labels (defaults to ':')
25764      */
25765     labelSeparator : ':',
25766     /**
25767      * @cfg {Boolean} hideLabels
25768      * True to suppress the display of field labels in this layout (defaults to false)
25769      */
25770     hideLabels : false,
25771
25772     // private
25773     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25774     
25775     isLayout : true,
25776     
25777     // private
25778     onRender : function(ct, position){
25779         if(this.el){ // from markup
25780             this.el = Roo.get(this.el);
25781         }else {  // generate
25782             var cfg = this.getAutoCreate();
25783             this.el = ct.createChild(cfg, position);
25784         }
25785         if(this.style){
25786             this.el.applyStyles(this.style);
25787         }
25788         if(this.labelAlign){
25789             this.el.addClass('x-form-label-'+this.labelAlign);
25790         }
25791         if(this.hideLabels){
25792             this.labelStyle = "display:none";
25793             this.elementStyle = "padding-left:0;";
25794         }else{
25795             if(typeof this.labelWidth == 'number'){
25796                 this.labelStyle = "width:"+this.labelWidth+"px;";
25797                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25798             }
25799             if(this.labelAlign == 'top'){
25800                 this.labelStyle = "width:auto;";
25801                 this.elementStyle = "padding-left:0;";
25802             }
25803         }
25804         var stack = this.stack;
25805         var slen = stack.length;
25806         if(slen > 0){
25807             if(!this.fieldTpl){
25808                 var t = new Roo.Template(
25809                     '<div class="x-form-item {5}">',
25810                         '<label for="{0}" style="{2}">{1}{4}</label>',
25811                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25812                         '</div>',
25813                     '</div><div class="x-form-clear-left"></div>'
25814                 );
25815                 t.disableFormats = true;
25816                 t.compile();
25817                 Roo.form.Layout.prototype.fieldTpl = t;
25818             }
25819             for(var i = 0; i < slen; i++) {
25820                 if(stack[i].isFormField){
25821                     this.renderField(stack[i]);
25822                 }else{
25823                     this.renderComponent(stack[i]);
25824                 }
25825             }
25826         }
25827         if(this.clear){
25828             this.el.createChild({cls:'x-form-clear'});
25829         }
25830     },
25831
25832     // private
25833     renderField : function(f){
25834         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25835                f.id, //0
25836                f.fieldLabel, //1
25837                f.labelStyle||this.labelStyle||'', //2
25838                this.elementStyle||'', //3
25839                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25840                f.itemCls||this.itemCls||''  //5
25841        ], true).getPrevSibling());
25842     },
25843
25844     // private
25845     renderComponent : function(c){
25846         c.render(c.isLayout ? this.el : this.el.createChild());    
25847     },
25848     /**
25849      * Adds a object form elements (using the xtype property as the factory method.)
25850      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25851      * @param {Object} config 
25852      */
25853     addxtype : function(o)
25854     {
25855         // create the lement.
25856         o.form = this.form;
25857         var fe = Roo.factory(o, Roo.form);
25858         this.form.allItems.push(fe);
25859         this.stack.push(fe);
25860         
25861         if (fe.isFormField) {
25862             this.form.items.add(fe);
25863         }
25864          
25865         return fe;
25866     }
25867 });
25868
25869 /**
25870  * @class Roo.form.Column
25871  * @extends Roo.form.Layout
25872  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25873  * @constructor
25874  * @param {Object} config Configuration options
25875  */
25876 Roo.form.Column = function(config){
25877     Roo.form.Column.superclass.constructor.call(this, config);
25878 };
25879
25880 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25881     /**
25882      * @cfg {Number/String} width
25883      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25884      */
25885     /**
25886      * @cfg {String/Object} autoCreate
25887      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25888      */
25889
25890     // private
25891     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25892
25893     // private
25894     onRender : function(ct, position){
25895         Roo.form.Column.superclass.onRender.call(this, ct, position);
25896         if(this.width){
25897             this.el.setWidth(this.width);
25898         }
25899     }
25900 });
25901
25902
25903 /**
25904  * @class Roo.form.Row
25905  * @extends Roo.form.Layout
25906  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25907  * @constructor
25908  * @param {Object} config Configuration options
25909  */
25910
25911  
25912 Roo.form.Row = function(config){
25913     Roo.form.Row.superclass.constructor.call(this, config);
25914 };
25915  
25916 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25917       /**
25918      * @cfg {Number/String} width
25919      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25920      */
25921     /**
25922      * @cfg {Number/String} height
25923      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25924      */
25925     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25926     
25927     padWidth : 20,
25928     // private
25929     onRender : function(ct, position){
25930         //console.log('row render');
25931         if(!this.rowTpl){
25932             var t = new Roo.Template(
25933                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25934                     '<label for="{0}" style="{2}">{1}{4}</label>',
25935                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25936                     '</div>',
25937                 '</div>'
25938             );
25939             t.disableFormats = true;
25940             t.compile();
25941             Roo.form.Layout.prototype.rowTpl = t;
25942         }
25943         this.fieldTpl = this.rowTpl;
25944         
25945         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25946         var labelWidth = 100;
25947         
25948         if ((this.labelAlign != 'top')) {
25949             if (typeof this.labelWidth == 'number') {
25950                 labelWidth = this.labelWidth
25951             }
25952             this.padWidth =  20 + labelWidth;
25953             
25954         }
25955         
25956         Roo.form.Column.superclass.onRender.call(this, ct, position);
25957         if(this.width){
25958             this.el.setWidth(this.width);
25959         }
25960         if(this.height){
25961             this.el.setHeight(this.height);
25962         }
25963     },
25964     
25965     // private
25966     renderField : function(f){
25967         f.fieldEl = this.fieldTpl.append(this.el, [
25968                f.id, f.fieldLabel,
25969                f.labelStyle||this.labelStyle||'',
25970                this.elementStyle||'',
25971                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25972                f.itemCls||this.itemCls||'',
25973                f.width ? f.width + this.padWidth : 160 + this.padWidth
25974        ],true);
25975     }
25976 });
25977  
25978
25979 /**
25980  * @class Roo.form.FieldSet
25981  * @extends Roo.form.Layout
25982  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25983  * @constructor
25984  * @param {Object} config Configuration options
25985  */
25986 Roo.form.FieldSet = function(config){
25987     Roo.form.FieldSet.superclass.constructor.call(this, config);
25988 };
25989
25990 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25991     /**
25992      * @cfg {String} legend
25993      * The text to display as the legend for the FieldSet (defaults to '')
25994      */
25995     /**
25996      * @cfg {String/Object} autoCreate
25997      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25998      */
25999
26000     // private
26001     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26002
26003     // private
26004     onRender : function(ct, position){
26005         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26006         if(this.legend){
26007             this.setLegend(this.legend);
26008         }
26009     },
26010
26011     // private
26012     setLegend : function(text){
26013         if(this.rendered){
26014             this.el.child('legend').update(text);
26015         }
26016     }
26017 });/*
26018  * Based on:
26019  * Ext JS Library 1.1.1
26020  * Copyright(c) 2006-2007, Ext JS, LLC.
26021  *
26022  * Originally Released Under LGPL - original licence link has changed is not relivant.
26023  *
26024  * Fork - LGPL
26025  * <script type="text/javascript">
26026  */
26027 /**
26028  * @class Roo.form.VTypes
26029  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26030  * @singleton
26031  */
26032 Roo.form.VTypes = function(){
26033     // closure these in so they are only created once.
26034     var alpha = /^[a-zA-Z_]+$/;
26035     var alphanum = /^[a-zA-Z0-9_]+$/;
26036     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26037     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26038
26039     // All these messages and functions are configurable
26040     return {
26041         /**
26042          * The function used to validate email addresses
26043          * @param {String} value The email address
26044          */
26045         'email' : function(v){
26046             return email.test(v);
26047         },
26048         /**
26049          * The error text to display when the email validation function returns false
26050          * @type String
26051          */
26052         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26053         /**
26054          * The keystroke filter mask to be applied on email input
26055          * @type RegExp
26056          */
26057         'emailMask' : /[a-z0-9_\.\-@]/i,
26058
26059         /**
26060          * The function used to validate URLs
26061          * @param {String} value The URL
26062          */
26063         'url' : function(v){
26064             return url.test(v);
26065         },
26066         /**
26067          * The error text to display when the url validation function returns false
26068          * @type String
26069          */
26070         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26071         
26072         /**
26073          * The function used to validate alpha values
26074          * @param {String} value The value
26075          */
26076         'alpha' : function(v){
26077             return alpha.test(v);
26078         },
26079         /**
26080          * The error text to display when the alpha validation function returns false
26081          * @type String
26082          */
26083         'alphaText' : 'This field should only contain letters and _',
26084         /**
26085          * The keystroke filter mask to be applied on alpha input
26086          * @type RegExp
26087          */
26088         'alphaMask' : /[a-z_]/i,
26089
26090         /**
26091          * The function used to validate alphanumeric values
26092          * @param {String} value The value
26093          */
26094         'alphanum' : function(v){
26095             return alphanum.test(v);
26096         },
26097         /**
26098          * The error text to display when the alphanumeric validation function returns false
26099          * @type String
26100          */
26101         'alphanumText' : 'This field should only contain letters, numbers and _',
26102         /**
26103          * The keystroke filter mask to be applied on alphanumeric input
26104          * @type RegExp
26105          */
26106         'alphanumMask' : /[a-z0-9_]/i
26107     };
26108 }();//<script type="text/javascript">
26109
26110 /**
26111  * @class Roo.form.FCKeditor
26112  * @extends Roo.form.TextArea
26113  * Wrapper around the FCKEditor http://www.fckeditor.net
26114  * @constructor
26115  * Creates a new FCKeditor
26116  * @param {Object} config Configuration options
26117  */
26118 Roo.form.FCKeditor = function(config){
26119     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26120     this.addEvents({
26121          /**
26122          * @event editorinit
26123          * Fired when the editor is initialized - you can add extra handlers here..
26124          * @param {FCKeditor} this
26125          * @param {Object} the FCK object.
26126          */
26127         editorinit : true
26128     });
26129     
26130     
26131 };
26132 Roo.form.FCKeditor.editors = { };
26133 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26134 {
26135     //defaultAutoCreate : {
26136     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26137     //},
26138     // private
26139     /**
26140      * @cfg {Object} fck options - see fck manual for details.
26141      */
26142     fckconfig : false,
26143     
26144     /**
26145      * @cfg {Object} fck toolbar set (Basic or Default)
26146      */
26147     toolbarSet : 'Basic',
26148     /**
26149      * @cfg {Object} fck BasePath
26150      */ 
26151     basePath : '/fckeditor/',
26152     
26153     
26154     frame : false,
26155     
26156     value : '',
26157     
26158    
26159     onRender : function(ct, position)
26160     {
26161         if(!this.el){
26162             this.defaultAutoCreate = {
26163                 tag: "textarea",
26164                 style:"width:300px;height:60px;",
26165                 autocomplete: "new-password"
26166             };
26167         }
26168         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26169         /*
26170         if(this.grow){
26171             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26172             if(this.preventScrollbars){
26173                 this.el.setStyle("overflow", "hidden");
26174             }
26175             this.el.setHeight(this.growMin);
26176         }
26177         */
26178         //console.log('onrender' + this.getId() );
26179         Roo.form.FCKeditor.editors[this.getId()] = this;
26180          
26181
26182         this.replaceTextarea() ;
26183         
26184     },
26185     
26186     getEditor : function() {
26187         return this.fckEditor;
26188     },
26189     /**
26190      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26191      * @param {Mixed} value The value to set
26192      */
26193     
26194     
26195     setValue : function(value)
26196     {
26197         //console.log('setValue: ' + value);
26198         
26199         if(typeof(value) == 'undefined') { // not sure why this is happending...
26200             return;
26201         }
26202         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26203         
26204         //if(!this.el || !this.getEditor()) {
26205         //    this.value = value;
26206             //this.setValue.defer(100,this,[value]);    
26207         //    return;
26208         //} 
26209         
26210         if(!this.getEditor()) {
26211             return;
26212         }
26213         
26214         this.getEditor().SetData(value);
26215         
26216         //
26217
26218     },
26219
26220     /**
26221      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26222      * @return {Mixed} value The field value
26223      */
26224     getValue : function()
26225     {
26226         
26227         if (this.frame && this.frame.dom.style.display == 'none') {
26228             return Roo.form.FCKeditor.superclass.getValue.call(this);
26229         }
26230         
26231         if(!this.el || !this.getEditor()) {
26232            
26233            // this.getValue.defer(100,this); 
26234             return this.value;
26235         }
26236        
26237         
26238         var value=this.getEditor().GetData();
26239         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26240         return Roo.form.FCKeditor.superclass.getValue.call(this);
26241         
26242
26243     },
26244
26245     /**
26246      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26247      * @return {Mixed} value The field value
26248      */
26249     getRawValue : function()
26250     {
26251         if (this.frame && this.frame.dom.style.display == 'none') {
26252             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26253         }
26254         
26255         if(!this.el || !this.getEditor()) {
26256             //this.getRawValue.defer(100,this); 
26257             return this.value;
26258             return;
26259         }
26260         
26261         
26262         
26263         var value=this.getEditor().GetData();
26264         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26265         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26266          
26267     },
26268     
26269     setSize : function(w,h) {
26270         
26271         
26272         
26273         //if (this.frame && this.frame.dom.style.display == 'none') {
26274         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26275         //    return;
26276         //}
26277         //if(!this.el || !this.getEditor()) {
26278         //    this.setSize.defer(100,this, [w,h]); 
26279         //    return;
26280         //}
26281         
26282         
26283         
26284         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26285         
26286         this.frame.dom.setAttribute('width', w);
26287         this.frame.dom.setAttribute('height', h);
26288         this.frame.setSize(w,h);
26289         
26290     },
26291     
26292     toggleSourceEdit : function(value) {
26293         
26294       
26295          
26296         this.el.dom.style.display = value ? '' : 'none';
26297         this.frame.dom.style.display = value ?  'none' : '';
26298         
26299     },
26300     
26301     
26302     focus: function(tag)
26303     {
26304         if (this.frame.dom.style.display == 'none') {
26305             return Roo.form.FCKeditor.superclass.focus.call(this);
26306         }
26307         if(!this.el || !this.getEditor()) {
26308             this.focus.defer(100,this, [tag]); 
26309             return;
26310         }
26311         
26312         
26313         
26314         
26315         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26316         this.getEditor().Focus();
26317         if (tgs.length) {
26318             if (!this.getEditor().Selection.GetSelection()) {
26319                 this.focus.defer(100,this, [tag]); 
26320                 return;
26321             }
26322             
26323             
26324             var r = this.getEditor().EditorDocument.createRange();
26325             r.setStart(tgs[0],0);
26326             r.setEnd(tgs[0],0);
26327             this.getEditor().Selection.GetSelection().removeAllRanges();
26328             this.getEditor().Selection.GetSelection().addRange(r);
26329             this.getEditor().Focus();
26330         }
26331         
26332     },
26333     
26334     
26335     
26336     replaceTextarea : function()
26337     {
26338         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26339             return ;
26340         }
26341         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26342         //{
26343             // We must check the elements firstly using the Id and then the name.
26344         var oTextarea = document.getElementById( this.getId() );
26345         
26346         var colElementsByName = document.getElementsByName( this.getId() ) ;
26347          
26348         oTextarea.style.display = 'none' ;
26349
26350         if ( oTextarea.tabIndex ) {            
26351             this.TabIndex = oTextarea.tabIndex ;
26352         }
26353         
26354         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26355         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26356         this.frame = Roo.get(this.getId() + '___Frame')
26357     },
26358     
26359     _getConfigHtml : function()
26360     {
26361         var sConfig = '' ;
26362
26363         for ( var o in this.fckconfig ) {
26364             sConfig += sConfig.length > 0  ? '&amp;' : '';
26365             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26366         }
26367
26368         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26369     },
26370     
26371     
26372     _getIFrameHtml : function()
26373     {
26374         var sFile = 'fckeditor.html' ;
26375         /* no idea what this is about..
26376         try
26377         {
26378             if ( (/fcksource=true/i).test( window.top.location.search ) )
26379                 sFile = 'fckeditor.original.html' ;
26380         }
26381         catch (e) { 
26382         */
26383
26384         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26385         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26386         
26387         
26388         var html = '<iframe id="' + this.getId() +
26389             '___Frame" src="' + sLink +
26390             '" width="' + this.width +
26391             '" height="' + this.height + '"' +
26392             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26393             ' frameborder="0" scrolling="no"></iframe>' ;
26394
26395         return html ;
26396     },
26397     
26398     _insertHtmlBefore : function( html, element )
26399     {
26400         if ( element.insertAdjacentHTML )       {
26401             // IE
26402             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26403         } else { // Gecko
26404             var oRange = document.createRange() ;
26405             oRange.setStartBefore( element ) ;
26406             var oFragment = oRange.createContextualFragment( html );
26407             element.parentNode.insertBefore( oFragment, element ) ;
26408         }
26409     }
26410     
26411     
26412   
26413     
26414     
26415     
26416     
26417
26418 });
26419
26420 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26421
26422 function FCKeditor_OnComplete(editorInstance){
26423     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26424     f.fckEditor = editorInstance;
26425     //console.log("loaded");
26426     f.fireEvent('editorinit', f, editorInstance);
26427
26428   
26429
26430  
26431
26432
26433
26434
26435
26436
26437
26438
26439
26440
26441
26442
26443
26444
26445
26446 //<script type="text/javascript">
26447 /**
26448  * @class Roo.form.GridField
26449  * @extends Roo.form.Field
26450  * Embed a grid (or editable grid into a form)
26451  * STATUS ALPHA
26452  * 
26453  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26454  * it needs 
26455  * xgrid.store = Roo.data.Store
26456  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26457  * xgrid.store.reader = Roo.data.JsonReader 
26458  * 
26459  * 
26460  * @constructor
26461  * Creates a new GridField
26462  * @param {Object} config Configuration options
26463  */
26464 Roo.form.GridField = function(config){
26465     Roo.form.GridField.superclass.constructor.call(this, config);
26466      
26467 };
26468
26469 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26470     /**
26471      * @cfg {Number} width  - used to restrict width of grid..
26472      */
26473     width : 100,
26474     /**
26475      * @cfg {Number} height - used to restrict height of grid..
26476      */
26477     height : 50,
26478      /**
26479      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26480          * 
26481          *}
26482      */
26483     xgrid : false, 
26484     /**
26485      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26486      * {tag: "input", type: "checkbox", autocomplete: "off"})
26487      */
26488    // defaultAutoCreate : { tag: 'div' },
26489     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26490     /**
26491      * @cfg {String} addTitle Text to include for adding a title.
26492      */
26493     addTitle : false,
26494     //
26495     onResize : function(){
26496         Roo.form.Field.superclass.onResize.apply(this, arguments);
26497     },
26498
26499     initEvents : function(){
26500         // Roo.form.Checkbox.superclass.initEvents.call(this);
26501         // has no events...
26502        
26503     },
26504
26505
26506     getResizeEl : function(){
26507         return this.wrap;
26508     },
26509
26510     getPositionEl : function(){
26511         return this.wrap;
26512     },
26513
26514     // private
26515     onRender : function(ct, position){
26516         
26517         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26518         var style = this.style;
26519         delete this.style;
26520         
26521         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26522         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26523         this.viewEl = this.wrap.createChild({ tag: 'div' });
26524         if (style) {
26525             this.viewEl.applyStyles(style);
26526         }
26527         if (this.width) {
26528             this.viewEl.setWidth(this.width);
26529         }
26530         if (this.height) {
26531             this.viewEl.setHeight(this.height);
26532         }
26533         //if(this.inputValue !== undefined){
26534         //this.setValue(this.value);
26535         
26536         
26537         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26538         
26539         
26540         this.grid.render();
26541         this.grid.getDataSource().on('remove', this.refreshValue, this);
26542         this.grid.getDataSource().on('update', this.refreshValue, this);
26543         this.grid.on('afteredit', this.refreshValue, this);
26544  
26545     },
26546      
26547     
26548     /**
26549      * Sets the value of the item. 
26550      * @param {String} either an object  or a string..
26551      */
26552     setValue : function(v){
26553         //this.value = v;
26554         v = v || []; // empty set..
26555         // this does not seem smart - it really only affects memoryproxy grids..
26556         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26557             var ds = this.grid.getDataSource();
26558             // assumes a json reader..
26559             var data = {}
26560             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26561             ds.loadData( data);
26562         }
26563         // clear selection so it does not get stale.
26564         if (this.grid.sm) { 
26565             this.grid.sm.clearSelections();
26566         }
26567         
26568         Roo.form.GridField.superclass.setValue.call(this, v);
26569         this.refreshValue();
26570         // should load data in the grid really....
26571     },
26572     
26573     // private
26574     refreshValue: function() {
26575          var val = [];
26576         this.grid.getDataSource().each(function(r) {
26577             val.push(r.data);
26578         });
26579         this.el.dom.value = Roo.encode(val);
26580     }
26581     
26582      
26583     
26584     
26585 });/*
26586  * Based on:
26587  * Ext JS Library 1.1.1
26588  * Copyright(c) 2006-2007, Ext JS, LLC.
26589  *
26590  * Originally Released Under LGPL - original licence link has changed is not relivant.
26591  *
26592  * Fork - LGPL
26593  * <script type="text/javascript">
26594  */
26595 /**
26596  * @class Roo.form.DisplayField
26597  * @extends Roo.form.Field
26598  * A generic Field to display non-editable data.
26599  * @cfg {Boolean} closable (true|false) default false
26600  * @constructor
26601  * Creates a new Display Field item.
26602  * @param {Object} config Configuration options
26603  */
26604 Roo.form.DisplayField = function(config){
26605     Roo.form.DisplayField.superclass.constructor.call(this, config);
26606     
26607     this.addEvents({
26608         /**
26609          * @event close
26610          * Fires after the click the close btn
26611              * @param {Roo.form.DisplayField} this
26612              */
26613         close : true
26614     });
26615 };
26616
26617 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26618     inputType:      'hidden',
26619     allowBlank:     true,
26620     readOnly:         true,
26621     
26622  
26623     /**
26624      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26625      */
26626     focusClass : undefined,
26627     /**
26628      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26629      */
26630     fieldClass: 'x-form-field',
26631     
26632      /**
26633      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26634      */
26635     valueRenderer: undefined,
26636     
26637     width: 100,
26638     /**
26639      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26640      * {tag: "input", type: "checkbox", autocomplete: "off"})
26641      */
26642      
26643  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26644  
26645     closable : false,
26646     
26647     onResize : function(){
26648         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26649         
26650     },
26651
26652     initEvents : function(){
26653         // Roo.form.Checkbox.superclass.initEvents.call(this);
26654         // has no events...
26655         
26656         if(this.closable){
26657             this.closeEl.on('click', this.onClose, this);
26658         }
26659        
26660     },
26661
26662
26663     getResizeEl : function(){
26664         return this.wrap;
26665     },
26666
26667     getPositionEl : function(){
26668         return this.wrap;
26669     },
26670
26671     // private
26672     onRender : function(ct, position){
26673         
26674         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26675         //if(this.inputValue !== undefined){
26676         this.wrap = this.el.wrap();
26677         
26678         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26679         
26680         if(this.closable){
26681             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26682         }
26683         
26684         if (this.bodyStyle) {
26685             this.viewEl.applyStyles(this.bodyStyle);
26686         }
26687         //this.viewEl.setStyle('padding', '2px');
26688         
26689         this.setValue(this.value);
26690         
26691     },
26692 /*
26693     // private
26694     initValue : Roo.emptyFn,
26695
26696   */
26697
26698         // private
26699     onClick : function(){
26700         
26701     },
26702
26703     /**
26704      * Sets the checked state of the checkbox.
26705      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26706      */
26707     setValue : function(v){
26708         this.value = v;
26709         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26710         // this might be called before we have a dom element..
26711         if (!this.viewEl) {
26712             return;
26713         }
26714         this.viewEl.dom.innerHTML = html;
26715         Roo.form.DisplayField.superclass.setValue.call(this, v);
26716
26717     },
26718     
26719     onClose : function(e)
26720     {
26721         e.preventDefault();
26722         
26723         this.fireEvent('close', this);
26724     }
26725 });/*
26726  * 
26727  * Licence- LGPL
26728  * 
26729  */
26730
26731 /**
26732  * @class Roo.form.DayPicker
26733  * @extends Roo.form.Field
26734  * A Day picker show [M] [T] [W] ....
26735  * @constructor
26736  * Creates a new Day Picker
26737  * @param {Object} config Configuration options
26738  */
26739 Roo.form.DayPicker= function(config){
26740     Roo.form.DayPicker.superclass.constructor.call(this, config);
26741      
26742 };
26743
26744 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26745     /**
26746      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26747      */
26748     focusClass : undefined,
26749     /**
26750      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26751      */
26752     fieldClass: "x-form-field",
26753    
26754     /**
26755      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26756      * {tag: "input", type: "checkbox", autocomplete: "off"})
26757      */
26758     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26759     
26760    
26761     actionMode : 'viewEl', 
26762     //
26763     // private
26764  
26765     inputType : 'hidden',
26766     
26767      
26768     inputElement: false, // real input element?
26769     basedOn: false, // ????
26770     
26771     isFormField: true, // not sure where this is needed!!!!
26772
26773     onResize : function(){
26774         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26775         if(!this.boxLabel){
26776             this.el.alignTo(this.wrap, 'c-c');
26777         }
26778     },
26779
26780     initEvents : function(){
26781         Roo.form.Checkbox.superclass.initEvents.call(this);
26782         this.el.on("click", this.onClick,  this);
26783         this.el.on("change", this.onClick,  this);
26784     },
26785
26786
26787     getResizeEl : function(){
26788         return this.wrap;
26789     },
26790
26791     getPositionEl : function(){
26792         return this.wrap;
26793     },
26794
26795     
26796     // private
26797     onRender : function(ct, position){
26798         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26799        
26800         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26801         
26802         var r1 = '<table><tr>';
26803         var r2 = '<tr class="x-form-daypick-icons">';
26804         for (var i=0; i < 7; i++) {
26805             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26806             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26807         }
26808         
26809         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26810         viewEl.select('img').on('click', this.onClick, this);
26811         this.viewEl = viewEl;   
26812         
26813         
26814         // this will not work on Chrome!!!
26815         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26816         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26817         
26818         
26819           
26820
26821     },
26822
26823     // private
26824     initValue : Roo.emptyFn,
26825
26826     /**
26827      * Returns the checked state of the checkbox.
26828      * @return {Boolean} True if checked, else false
26829      */
26830     getValue : function(){
26831         return this.el.dom.value;
26832         
26833     },
26834
26835         // private
26836     onClick : function(e){ 
26837         //this.setChecked(!this.checked);
26838         Roo.get(e.target).toggleClass('x-menu-item-checked');
26839         this.refreshValue();
26840         //if(this.el.dom.checked != this.checked){
26841         //    this.setValue(this.el.dom.checked);
26842        // }
26843     },
26844     
26845     // private
26846     refreshValue : function()
26847     {
26848         var val = '';
26849         this.viewEl.select('img',true).each(function(e,i,n)  {
26850             val += e.is(".x-menu-item-checked") ? String(n) : '';
26851         });
26852         this.setValue(val, true);
26853     },
26854
26855     /**
26856      * Sets the checked state of the checkbox.
26857      * On is always based on a string comparison between inputValue and the param.
26858      * @param {Boolean/String} value - the value to set 
26859      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26860      */
26861     setValue : function(v,suppressEvent){
26862         if (!this.el.dom) {
26863             return;
26864         }
26865         var old = this.el.dom.value ;
26866         this.el.dom.value = v;
26867         if (suppressEvent) {
26868             return ;
26869         }
26870          
26871         // update display..
26872         this.viewEl.select('img',true).each(function(e,i,n)  {
26873             
26874             var on = e.is(".x-menu-item-checked");
26875             var newv = v.indexOf(String(n)) > -1;
26876             if (on != newv) {
26877                 e.toggleClass('x-menu-item-checked');
26878             }
26879             
26880         });
26881         
26882         
26883         this.fireEvent('change', this, v, old);
26884         
26885         
26886     },
26887    
26888     // handle setting of hidden value by some other method!!?!?
26889     setFromHidden: function()
26890     {
26891         if(!this.el){
26892             return;
26893         }
26894         //console.log("SET FROM HIDDEN");
26895         //alert('setFrom hidden');
26896         this.setValue(this.el.dom.value);
26897     },
26898     
26899     onDestroy : function()
26900     {
26901         if(this.viewEl){
26902             Roo.get(this.viewEl).remove();
26903         }
26904          
26905         Roo.form.DayPicker.superclass.onDestroy.call(this);
26906     }
26907
26908 });/*
26909  * RooJS Library 1.1.1
26910  * Copyright(c) 2008-2011  Alan Knowles
26911  *
26912  * License - LGPL
26913  */
26914  
26915
26916 /**
26917  * @class Roo.form.ComboCheck
26918  * @extends Roo.form.ComboBox
26919  * A combobox for multiple select items.
26920  *
26921  * FIXME - could do with a reset button..
26922  * 
26923  * @constructor
26924  * Create a new ComboCheck
26925  * @param {Object} config Configuration options
26926  */
26927 Roo.form.ComboCheck = function(config){
26928     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26929     // should verify some data...
26930     // like
26931     // hiddenName = required..
26932     // displayField = required
26933     // valudField == required
26934     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26935     var _t = this;
26936     Roo.each(req, function(e) {
26937         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26938             throw "Roo.form.ComboCheck : missing value for: " + e;
26939         }
26940     });
26941     
26942     
26943 };
26944
26945 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26946      
26947      
26948     editable : false,
26949      
26950     selectedClass: 'x-menu-item-checked', 
26951     
26952     // private
26953     onRender : function(ct, position){
26954         var _t = this;
26955         
26956         
26957         
26958         if(!this.tpl){
26959             var cls = 'x-combo-list';
26960
26961             
26962             this.tpl =  new Roo.Template({
26963                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26964                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26965                    '<span>{' + this.displayField + '}</span>' +
26966                     '</div>' 
26967                 
26968             });
26969         }
26970  
26971         
26972         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26973         this.view.singleSelect = false;
26974         this.view.multiSelect = true;
26975         this.view.toggleSelect = true;
26976         this.pageTb.add(new Roo.Toolbar.Fill(), {
26977             
26978             text: 'Done',
26979             handler: function()
26980             {
26981                 _t.collapse();
26982             }
26983         });
26984     },
26985     
26986     onViewOver : function(e, t){
26987         // do nothing...
26988         return;
26989         
26990     },
26991     
26992     onViewClick : function(doFocus,index){
26993         return;
26994         
26995     },
26996     select: function () {
26997         //Roo.log("SELECT CALLED");
26998     },
26999      
27000     selectByValue : function(xv, scrollIntoView){
27001         var ar = this.getValueArray();
27002         var sels = [];
27003         
27004         Roo.each(ar, function(v) {
27005             if(v === undefined || v === null){
27006                 return;
27007             }
27008             var r = this.findRecord(this.valueField, v);
27009             if(r){
27010                 sels.push(this.store.indexOf(r))
27011                 
27012             }
27013         },this);
27014         this.view.select(sels);
27015         return false;
27016     },
27017     
27018     
27019     
27020     onSelect : function(record, index){
27021        // Roo.log("onselect Called");
27022        // this is only called by the clear button now..
27023         this.view.clearSelections();
27024         this.setValue('[]');
27025         if (this.value != this.valueBefore) {
27026             this.fireEvent('change', this, this.value, this.valueBefore);
27027             this.valueBefore = this.value;
27028         }
27029     },
27030     getValueArray : function()
27031     {
27032         var ar = [] ;
27033         
27034         try {
27035             //Roo.log(this.value);
27036             if (typeof(this.value) == 'undefined') {
27037                 return [];
27038             }
27039             var ar = Roo.decode(this.value);
27040             return  ar instanceof Array ? ar : []; //?? valid?
27041             
27042         } catch(e) {
27043             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27044             return [];
27045         }
27046          
27047     },
27048     expand : function ()
27049     {
27050         
27051         Roo.form.ComboCheck.superclass.expand.call(this);
27052         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27053         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27054         
27055
27056     },
27057     
27058     collapse : function(){
27059         Roo.form.ComboCheck.superclass.collapse.call(this);
27060         var sl = this.view.getSelectedIndexes();
27061         var st = this.store;
27062         var nv = [];
27063         var tv = [];
27064         var r;
27065         Roo.each(sl, function(i) {
27066             r = st.getAt(i);
27067             nv.push(r.get(this.valueField));
27068         },this);
27069         this.setValue(Roo.encode(nv));
27070         if (this.value != this.valueBefore) {
27071
27072             this.fireEvent('change', this, this.value, this.valueBefore);
27073             this.valueBefore = this.value;
27074         }
27075         
27076     },
27077     
27078     setValue : function(v){
27079         // Roo.log(v);
27080         this.value = v;
27081         
27082         var vals = this.getValueArray();
27083         var tv = [];
27084         Roo.each(vals, function(k) {
27085             var r = this.findRecord(this.valueField, k);
27086             if(r){
27087                 tv.push(r.data[this.displayField]);
27088             }else if(this.valueNotFoundText !== undefined){
27089                 tv.push( this.valueNotFoundText );
27090             }
27091         },this);
27092        // Roo.log(tv);
27093         
27094         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27095         this.hiddenField.value = v;
27096         this.value = v;
27097     }
27098     
27099 });/*
27100  * Based on:
27101  * Ext JS Library 1.1.1
27102  * Copyright(c) 2006-2007, Ext JS, LLC.
27103  *
27104  * Originally Released Under LGPL - original licence link has changed is not relivant.
27105  *
27106  * Fork - LGPL
27107  * <script type="text/javascript">
27108  */
27109  
27110 /**
27111  * @class Roo.form.Signature
27112  * @extends Roo.form.Field
27113  * Signature field.  
27114  * @constructor
27115  * 
27116  * @param {Object} config Configuration options
27117  */
27118
27119 Roo.form.Signature = function(config){
27120     Roo.form.Signature.superclass.constructor.call(this, config);
27121     
27122     this.addEvents({// not in used??
27123          /**
27124          * @event confirm
27125          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27126              * @param {Roo.form.Signature} combo This combo box
27127              */
27128         'confirm' : true,
27129         /**
27130          * @event reset
27131          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27132              * @param {Roo.form.ComboBox} combo This combo box
27133              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27134              */
27135         'reset' : true
27136     });
27137 };
27138
27139 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27140     /**
27141      * @cfg {Object} labels Label to use when rendering a form.
27142      * defaults to 
27143      * labels : { 
27144      *      clear : "Clear",
27145      *      confirm : "Confirm"
27146      *  }
27147      */
27148     labels : { 
27149         clear : "Clear",
27150         confirm : "Confirm"
27151     },
27152     /**
27153      * @cfg {Number} width The signature panel width (defaults to 300)
27154      */
27155     width: 300,
27156     /**
27157      * @cfg {Number} height The signature panel height (defaults to 100)
27158      */
27159     height : 100,
27160     /**
27161      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27162      */
27163     allowBlank : false,
27164     
27165     //private
27166     // {Object} signPanel The signature SVG panel element (defaults to {})
27167     signPanel : {},
27168     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27169     isMouseDown : false,
27170     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27171     isConfirmed : false,
27172     // {String} signatureTmp SVG mapping string (defaults to empty string)
27173     signatureTmp : '',
27174     
27175     
27176     defaultAutoCreate : { // modified by initCompnoent..
27177         tag: "input",
27178         type:"hidden"
27179     },
27180
27181     // private
27182     onRender : function(ct, position){
27183         
27184         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27185         
27186         this.wrap = this.el.wrap({
27187             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27188         });
27189         
27190         this.createToolbar(this);
27191         this.signPanel = this.wrap.createChild({
27192                 tag: 'div',
27193                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27194             }, this.el
27195         );
27196             
27197         this.svgID = Roo.id();
27198         this.svgEl = this.signPanel.createChild({
27199               xmlns : 'http://www.w3.org/2000/svg',
27200               tag : 'svg',
27201               id : this.svgID + "-svg",
27202               width: this.width,
27203               height: this.height,
27204               viewBox: '0 0 '+this.width+' '+this.height,
27205               cn : [
27206                 {
27207                     tag: "rect",
27208                     id: this.svgID + "-svg-r",
27209                     width: this.width,
27210                     height: this.height,
27211                     fill: "#ffa"
27212                 },
27213                 {
27214                     tag: "line",
27215                     id: this.svgID + "-svg-l",
27216                     x1: "0", // start
27217                     y1: (this.height*0.8), // start set the line in 80% of height
27218                     x2: this.width, // end
27219                     y2: (this.height*0.8), // end set the line in 80% of height
27220                     'stroke': "#666",
27221                     'stroke-width': "1",
27222                     'stroke-dasharray': "3",
27223                     'shape-rendering': "crispEdges",
27224                     'pointer-events': "none"
27225                 },
27226                 {
27227                     tag: "path",
27228                     id: this.svgID + "-svg-p",
27229                     'stroke': "navy",
27230                     'stroke-width': "3",
27231                     'fill': "none",
27232                     'pointer-events': 'none'
27233                 }
27234               ]
27235         });
27236         this.createSVG();
27237         this.svgBox = this.svgEl.dom.getScreenCTM();
27238     },
27239     createSVG : function(){ 
27240         var svg = this.signPanel;
27241         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27242         var t = this;
27243
27244         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27245         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27246         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27247         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27248         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27249         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27250         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27251         
27252     },
27253     isTouchEvent : function(e){
27254         return e.type.match(/^touch/);
27255     },
27256     getCoords : function (e) {
27257         var pt    = this.svgEl.dom.createSVGPoint();
27258         pt.x = e.clientX; 
27259         pt.y = e.clientY;
27260         if (this.isTouchEvent(e)) {
27261             pt.x =  e.targetTouches[0].clientX;
27262             pt.y = e.targetTouches[0].clientY;
27263         }
27264         var a = this.svgEl.dom.getScreenCTM();
27265         var b = a.inverse();
27266         var mx = pt.matrixTransform(b);
27267         return mx.x + ',' + mx.y;
27268     },
27269     //mouse event headler 
27270     down : function (e) {
27271         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27272         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27273         
27274         this.isMouseDown = true;
27275         
27276         e.preventDefault();
27277     },
27278     move : function (e) {
27279         if (this.isMouseDown) {
27280             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27281             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27282         }
27283         
27284         e.preventDefault();
27285     },
27286     up : function (e) {
27287         this.isMouseDown = false;
27288         var sp = this.signatureTmp.split(' ');
27289         
27290         if(sp.length > 1){
27291             if(!sp[sp.length-2].match(/^L/)){
27292                 sp.pop();
27293                 sp.pop();
27294                 sp.push("");
27295                 this.signatureTmp = sp.join(" ");
27296             }
27297         }
27298         if(this.getValue() != this.signatureTmp){
27299             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27300             this.isConfirmed = false;
27301         }
27302         e.preventDefault();
27303     },
27304     
27305     /**
27306      * Protected method that will not generally be called directly. It
27307      * is called when the editor creates its toolbar. Override this method if you need to
27308      * add custom toolbar buttons.
27309      * @param {HtmlEditor} editor
27310      */
27311     createToolbar : function(editor){
27312          function btn(id, toggle, handler){
27313             var xid = fid + '-'+ id ;
27314             return {
27315                 id : xid,
27316                 cmd : id,
27317                 cls : 'x-btn-icon x-edit-'+id,
27318                 enableToggle:toggle !== false,
27319                 scope: editor, // was editor...
27320                 handler:handler||editor.relayBtnCmd,
27321                 clickEvent:'mousedown',
27322                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27323                 tabIndex:-1
27324             };
27325         }
27326         
27327         
27328         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27329         this.tb = tb;
27330         this.tb.add(
27331            {
27332                 cls : ' x-signature-btn x-signature-'+id,
27333                 scope: editor, // was editor...
27334                 handler: this.reset,
27335                 clickEvent:'mousedown',
27336                 text: this.labels.clear
27337             },
27338             {
27339                  xtype : 'Fill',
27340                  xns: Roo.Toolbar
27341             }, 
27342             {
27343                 cls : '  x-signature-btn x-signature-'+id,
27344                 scope: editor, // was editor...
27345                 handler: this.confirmHandler,
27346                 clickEvent:'mousedown',
27347                 text: this.labels.confirm
27348             }
27349         );
27350     
27351     },
27352     //public
27353     /**
27354      * when user is clicked confirm then show this image.....
27355      * 
27356      * @return {String} Image Data URI
27357      */
27358     getImageDataURI : function(){
27359         var svg = this.svgEl.dom.parentNode.innerHTML;
27360         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27361         return src; 
27362     },
27363     /**
27364      * 
27365      * @return {Boolean} this.isConfirmed
27366      */
27367     getConfirmed : function(){
27368         return this.isConfirmed;
27369     },
27370     /**
27371      * 
27372      * @return {Number} this.width
27373      */
27374     getWidth : function(){
27375         return this.width;
27376     },
27377     /**
27378      * 
27379      * @return {Number} this.height
27380      */
27381     getHeight : function(){
27382         return this.height;
27383     },
27384     // private
27385     getSignature : function(){
27386         return this.signatureTmp;
27387     },
27388     // private
27389     reset : function(){
27390         this.signatureTmp = '';
27391         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27392         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27393         this.isConfirmed = false;
27394         Roo.form.Signature.superclass.reset.call(this);
27395     },
27396     setSignature : function(s){
27397         this.signatureTmp = s;
27398         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27399         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27400         this.setValue(s);
27401         this.isConfirmed = false;
27402         Roo.form.Signature.superclass.reset.call(this);
27403     }, 
27404     test : function(){
27405 //        Roo.log(this.signPanel.dom.contentWindow.up())
27406     },
27407     //private
27408     setConfirmed : function(){
27409         
27410         
27411         
27412 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27413     },
27414     // private
27415     confirmHandler : function(){
27416         if(!this.getSignature()){
27417             return;
27418         }
27419         
27420         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27421         this.setValue(this.getSignature());
27422         this.isConfirmed = true;
27423         
27424         this.fireEvent('confirm', this);
27425     },
27426     // private
27427     // Subclasses should provide the validation implementation by overriding this
27428     validateValue : function(value){
27429         if(this.allowBlank){
27430             return true;
27431         }
27432         
27433         if(this.isConfirmed){
27434             return true;
27435         }
27436         return false;
27437     }
27438 });/*
27439  * Based on:
27440  * Ext JS Library 1.1.1
27441  * Copyright(c) 2006-2007, Ext JS, LLC.
27442  *
27443  * Originally Released Under LGPL - original licence link has changed is not relivant.
27444  *
27445  * Fork - LGPL
27446  * <script type="text/javascript">
27447  */
27448  
27449
27450 /**
27451  * @class Roo.form.ComboBox
27452  * @extends Roo.form.TriggerField
27453  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27454  * @constructor
27455  * Create a new ComboBox.
27456  * @param {Object} config Configuration options
27457  */
27458 Roo.form.Select = function(config){
27459     Roo.form.Select.superclass.constructor.call(this, config);
27460      
27461 };
27462
27463 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27464     /**
27465      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27466      */
27467     /**
27468      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27469      * rendering into an Roo.Editor, defaults to false)
27470      */
27471     /**
27472      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27473      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27474      */
27475     /**
27476      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27477      */
27478     /**
27479      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27480      * the dropdown list (defaults to undefined, with no header element)
27481      */
27482
27483      /**
27484      * @cfg {String/Roo.Template} tpl The template to use to render the output
27485      */
27486      
27487     // private
27488     defaultAutoCreate : {tag: "select"  },
27489     /**
27490      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27491      */
27492     listWidth: undefined,
27493     /**
27494      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27495      * mode = 'remote' or 'text' if mode = 'local')
27496      */
27497     displayField: undefined,
27498     /**
27499      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27500      * mode = 'remote' or 'value' if mode = 'local'). 
27501      * Note: use of a valueField requires the user make a selection
27502      * in order for a value to be mapped.
27503      */
27504     valueField: undefined,
27505     
27506     
27507     /**
27508      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27509      * field's data value (defaults to the underlying DOM element's name)
27510      */
27511     hiddenName: undefined,
27512     /**
27513      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27514      */
27515     listClass: '',
27516     /**
27517      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27518      */
27519     selectedClass: 'x-combo-selected',
27520     /**
27521      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27522      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27523      * which displays a downward arrow icon).
27524      */
27525     triggerClass : 'x-form-arrow-trigger',
27526     /**
27527      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27528      */
27529     shadow:'sides',
27530     /**
27531      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27532      * anchor positions (defaults to 'tl-bl')
27533      */
27534     listAlign: 'tl-bl?',
27535     /**
27536      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27537      */
27538     maxHeight: 300,
27539     /**
27540      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27541      * query specified by the allQuery config option (defaults to 'query')
27542      */
27543     triggerAction: 'query',
27544     /**
27545      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27546      * (defaults to 4, does not apply if editable = false)
27547      */
27548     minChars : 4,
27549     /**
27550      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27551      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27552      */
27553     typeAhead: false,
27554     /**
27555      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27556      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27557      */
27558     queryDelay: 500,
27559     /**
27560      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27561      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27562      */
27563     pageSize: 0,
27564     /**
27565      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27566      * when editable = true (defaults to false)
27567      */
27568     selectOnFocus:false,
27569     /**
27570      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27571      */
27572     queryParam: 'query',
27573     /**
27574      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27575      * when mode = 'remote' (defaults to 'Loading...')
27576      */
27577     loadingText: 'Loading...',
27578     /**
27579      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27580      */
27581     resizable: false,
27582     /**
27583      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27584      */
27585     handleHeight : 8,
27586     /**
27587      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27588      * traditional select (defaults to true)
27589      */
27590     editable: true,
27591     /**
27592      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27593      */
27594     allQuery: '',
27595     /**
27596      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27597      */
27598     mode: 'remote',
27599     /**
27600      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27601      * listWidth has a higher value)
27602      */
27603     minListWidth : 70,
27604     /**
27605      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27606      * allow the user to set arbitrary text into the field (defaults to false)
27607      */
27608     forceSelection:false,
27609     /**
27610      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27611      * if typeAhead = true (defaults to 250)
27612      */
27613     typeAheadDelay : 250,
27614     /**
27615      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27616      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27617      */
27618     valueNotFoundText : undefined,
27619     
27620     /**
27621      * @cfg {String} defaultValue The value displayed after loading the store.
27622      */
27623     defaultValue: '',
27624     
27625     /**
27626      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27627      */
27628     blockFocus : false,
27629     
27630     /**
27631      * @cfg {Boolean} disableClear Disable showing of clear button.
27632      */
27633     disableClear : false,
27634     /**
27635      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27636      */
27637     alwaysQuery : false,
27638     
27639     //private
27640     addicon : false,
27641     editicon: false,
27642     
27643     // element that contains real text value.. (when hidden is used..)
27644      
27645     // private
27646     onRender : function(ct, position){
27647         Roo.form.Field.prototype.onRender.call(this, ct, position);
27648         
27649         if(this.store){
27650             this.store.on('beforeload', this.onBeforeLoad, this);
27651             this.store.on('load', this.onLoad, this);
27652             this.store.on('loadexception', this.onLoadException, this);
27653             this.store.load({});
27654         }
27655         
27656         
27657         
27658     },
27659
27660     // private
27661     initEvents : function(){
27662         //Roo.form.ComboBox.superclass.initEvents.call(this);
27663  
27664     },
27665
27666     onDestroy : function(){
27667        
27668         if(this.store){
27669             this.store.un('beforeload', this.onBeforeLoad, this);
27670             this.store.un('load', this.onLoad, this);
27671             this.store.un('loadexception', this.onLoadException, this);
27672         }
27673         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27674     },
27675
27676     // private
27677     fireKey : function(e){
27678         if(e.isNavKeyPress() && !this.list.isVisible()){
27679             this.fireEvent("specialkey", this, e);
27680         }
27681     },
27682
27683     // private
27684     onResize: function(w, h){
27685         
27686         return; 
27687     
27688         
27689     },
27690
27691     /**
27692      * Allow or prevent the user from directly editing the field text.  If false is passed,
27693      * the user will only be able to select from the items defined in the dropdown list.  This method
27694      * is the runtime equivalent of setting the 'editable' config option at config time.
27695      * @param {Boolean} value True to allow the user to directly edit the field text
27696      */
27697     setEditable : function(value){
27698          
27699     },
27700
27701     // private
27702     onBeforeLoad : function(){
27703         
27704         Roo.log("Select before load");
27705         return;
27706     
27707         this.innerList.update(this.loadingText ?
27708                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27709         //this.restrictHeight();
27710         this.selectedIndex = -1;
27711     },
27712
27713     // private
27714     onLoad : function(){
27715
27716     
27717         var dom = this.el.dom;
27718         dom.innerHTML = '';
27719          var od = dom.ownerDocument;
27720          
27721         if (this.emptyText) {
27722             var op = od.createElement('option');
27723             op.setAttribute('value', '');
27724             op.innerHTML = String.format('{0}', this.emptyText);
27725             dom.appendChild(op);
27726         }
27727         if(this.store.getCount() > 0){
27728            
27729             var vf = this.valueField;
27730             var df = this.displayField;
27731             this.store.data.each(function(r) {
27732                 // which colmsn to use... testing - cdoe / title..
27733                 var op = od.createElement('option');
27734                 op.setAttribute('value', r.data[vf]);
27735                 op.innerHTML = String.format('{0}', r.data[df]);
27736                 dom.appendChild(op);
27737             });
27738             if (typeof(this.defaultValue != 'undefined')) {
27739                 this.setValue(this.defaultValue);
27740             }
27741             
27742              
27743         }else{
27744             //this.onEmptyResults();
27745         }
27746         //this.el.focus();
27747     },
27748     // private
27749     onLoadException : function()
27750     {
27751         dom.innerHTML = '';
27752             
27753         Roo.log("Select on load exception");
27754         return;
27755     
27756         this.collapse();
27757         Roo.log(this.store.reader.jsonData);
27758         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27759             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27760         }
27761         
27762         
27763     },
27764     // private
27765     onTypeAhead : function(){
27766          
27767     },
27768
27769     // private
27770     onSelect : function(record, index){
27771         Roo.log('on select?');
27772         return;
27773         if(this.fireEvent('beforeselect', this, record, index) !== false){
27774             this.setFromData(index > -1 ? record.data : false);
27775             this.collapse();
27776             this.fireEvent('select', this, record, index);
27777         }
27778     },
27779
27780     /**
27781      * Returns the currently selected field value or empty string if no value is set.
27782      * @return {String} value The selected value
27783      */
27784     getValue : function(){
27785         var dom = this.el.dom;
27786         this.value = dom.options[dom.selectedIndex].value;
27787         return this.value;
27788         
27789     },
27790
27791     /**
27792      * Clears any text/value currently set in the field
27793      */
27794     clearValue : function(){
27795         this.value = '';
27796         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27797         
27798     },
27799
27800     /**
27801      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27802      * will be displayed in the field.  If the value does not match the data value of an existing item,
27803      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27804      * Otherwise the field will be blank (although the value will still be set).
27805      * @param {String} value The value to match
27806      */
27807     setValue : function(v){
27808         var d = this.el.dom;
27809         for (var i =0; i < d.options.length;i++) {
27810             if (v == d.options[i].value) {
27811                 d.selectedIndex = i;
27812                 this.value = v;
27813                 return;
27814             }
27815         }
27816         this.clearValue();
27817     },
27818     /**
27819      * @property {Object} the last set data for the element
27820      */
27821     
27822     lastData : false,
27823     /**
27824      * Sets the value of the field based on a object which is related to the record format for the store.
27825      * @param {Object} value the value to set as. or false on reset?
27826      */
27827     setFromData : function(o){
27828         Roo.log('setfrom data?');
27829          
27830         
27831         
27832     },
27833     // private
27834     reset : function(){
27835         this.clearValue();
27836     },
27837     // private
27838     findRecord : function(prop, value){
27839         
27840         return false;
27841     
27842         var record;
27843         if(this.store.getCount() > 0){
27844             this.store.each(function(r){
27845                 if(r.data[prop] == value){
27846                     record = r;
27847                     return false;
27848                 }
27849                 return true;
27850             });
27851         }
27852         return record;
27853     },
27854     
27855     getName: function()
27856     {
27857         // returns hidden if it's set..
27858         if (!this.rendered) {return ''};
27859         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27860         
27861     },
27862      
27863
27864     
27865
27866     // private
27867     onEmptyResults : function(){
27868         Roo.log('empty results');
27869         //this.collapse();
27870     },
27871
27872     /**
27873      * Returns true if the dropdown list is expanded, else false.
27874      */
27875     isExpanded : function(){
27876         return false;
27877     },
27878
27879     /**
27880      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27881      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27882      * @param {String} value The data value of the item to select
27883      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27884      * selected item if it is not currently in view (defaults to true)
27885      * @return {Boolean} True if the value matched an item in the list, else false
27886      */
27887     selectByValue : function(v, scrollIntoView){
27888         Roo.log('select By Value');
27889         return false;
27890     
27891         if(v !== undefined && v !== null){
27892             var r = this.findRecord(this.valueField || this.displayField, v);
27893             if(r){
27894                 this.select(this.store.indexOf(r), scrollIntoView);
27895                 return true;
27896             }
27897         }
27898         return false;
27899     },
27900
27901     /**
27902      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27903      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27904      * @param {Number} index The zero-based index of the list item to select
27905      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27906      * selected item if it is not currently in view (defaults to true)
27907      */
27908     select : function(index, scrollIntoView){
27909         Roo.log('select ');
27910         return  ;
27911         
27912         this.selectedIndex = index;
27913         this.view.select(index);
27914         if(scrollIntoView !== false){
27915             var el = this.view.getNode(index);
27916             if(el){
27917                 this.innerList.scrollChildIntoView(el, false);
27918             }
27919         }
27920     },
27921
27922       
27923
27924     // private
27925     validateBlur : function(){
27926         
27927         return;
27928         
27929     },
27930
27931     // private
27932     initQuery : function(){
27933         this.doQuery(this.getRawValue());
27934     },
27935
27936     // private
27937     doForce : function(){
27938         if(this.el.dom.value.length > 0){
27939             this.el.dom.value =
27940                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27941              
27942         }
27943     },
27944
27945     /**
27946      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27947      * query allowing the query action to be canceled if needed.
27948      * @param {String} query The SQL query to execute
27949      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27950      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27951      * saved in the current store (defaults to false)
27952      */
27953     doQuery : function(q, forceAll){
27954         
27955         Roo.log('doQuery?');
27956         if(q === undefined || q === null){
27957             q = '';
27958         }
27959         var qe = {
27960             query: q,
27961             forceAll: forceAll,
27962             combo: this,
27963             cancel:false
27964         };
27965         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27966             return false;
27967         }
27968         q = qe.query;
27969         forceAll = qe.forceAll;
27970         if(forceAll === true || (q.length >= this.minChars)){
27971             if(this.lastQuery != q || this.alwaysQuery){
27972                 this.lastQuery = q;
27973                 if(this.mode == 'local'){
27974                     this.selectedIndex = -1;
27975                     if(forceAll){
27976                         this.store.clearFilter();
27977                     }else{
27978                         this.store.filter(this.displayField, q);
27979                     }
27980                     this.onLoad();
27981                 }else{
27982                     this.store.baseParams[this.queryParam] = q;
27983                     this.store.load({
27984                         params: this.getParams(q)
27985                     });
27986                     this.expand();
27987                 }
27988             }else{
27989                 this.selectedIndex = -1;
27990                 this.onLoad();   
27991             }
27992         }
27993     },
27994
27995     // private
27996     getParams : function(q){
27997         var p = {};
27998         //p[this.queryParam] = q;
27999         if(this.pageSize){
28000             p.start = 0;
28001             p.limit = this.pageSize;
28002         }
28003         return p;
28004     },
28005
28006     /**
28007      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28008      */
28009     collapse : function(){
28010         
28011     },
28012
28013     // private
28014     collapseIf : function(e){
28015         
28016     },
28017
28018     /**
28019      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28020      */
28021     expand : function(){
28022         
28023     } ,
28024
28025     // private
28026      
28027
28028     /** 
28029     * @cfg {Boolean} grow 
28030     * @hide 
28031     */
28032     /** 
28033     * @cfg {Number} growMin 
28034     * @hide 
28035     */
28036     /** 
28037     * @cfg {Number} growMax 
28038     * @hide 
28039     */
28040     /**
28041      * @hide
28042      * @method autoSize
28043      */
28044     
28045     setWidth : function()
28046     {
28047         
28048     },
28049     getResizeEl : function(){
28050         return this.el;
28051     }
28052 });//<script type="text/javasscript">
28053  
28054
28055 /**
28056  * @class Roo.DDView
28057  * A DnD enabled version of Roo.View.
28058  * @param {Element/String} container The Element in which to create the View.
28059  * @param {String} tpl The template string used to create the markup for each element of the View
28060  * @param {Object} config The configuration properties. These include all the config options of
28061  * {@link Roo.View} plus some specific to this class.<br>
28062  * <p>
28063  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28064  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28065  * <p>
28066  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28067 .x-view-drag-insert-above {
28068         border-top:1px dotted #3366cc;
28069 }
28070 .x-view-drag-insert-below {
28071         border-bottom:1px dotted #3366cc;
28072 }
28073 </code></pre>
28074  * 
28075  */
28076  
28077 Roo.DDView = function(container, tpl, config) {
28078     Roo.DDView.superclass.constructor.apply(this, arguments);
28079     this.getEl().setStyle("outline", "0px none");
28080     this.getEl().unselectable();
28081     if (this.dragGroup) {
28082                 this.setDraggable(this.dragGroup.split(","));
28083     }
28084     if (this.dropGroup) {
28085                 this.setDroppable(this.dropGroup.split(","));
28086     }
28087     if (this.deletable) {
28088         this.setDeletable();
28089     }
28090     this.isDirtyFlag = false;
28091         this.addEvents({
28092                 "drop" : true
28093         });
28094 };
28095
28096 Roo.extend(Roo.DDView, Roo.View, {
28097 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28098 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28099 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28100 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28101
28102         isFormField: true,
28103
28104         reset: Roo.emptyFn,
28105         
28106         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28107
28108         validate: function() {
28109                 return true;
28110         },
28111         
28112         destroy: function() {
28113                 this.purgeListeners();
28114                 this.getEl.removeAllListeners();
28115                 this.getEl().remove();
28116                 if (this.dragZone) {
28117                         if (this.dragZone.destroy) {
28118                                 this.dragZone.destroy();
28119                         }
28120                 }
28121                 if (this.dropZone) {
28122                         if (this.dropZone.destroy) {
28123                                 this.dropZone.destroy();
28124                         }
28125                 }
28126         },
28127
28128 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28129         getName: function() {
28130                 return this.name;
28131         },
28132
28133 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28134         setValue: function(v) {
28135                 if (!this.store) {
28136                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28137                 }
28138                 var data = {};
28139                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28140                 this.store.proxy = new Roo.data.MemoryProxy(data);
28141                 this.store.load();
28142         },
28143
28144 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28145         getValue: function() {
28146                 var result = '(';
28147                 this.store.each(function(rec) {
28148                         result += rec.id + ',';
28149                 });
28150                 return result.substr(0, result.length - 1) + ')';
28151         },
28152         
28153         getIds: function() {
28154                 var i = 0, result = new Array(this.store.getCount());
28155                 this.store.each(function(rec) {
28156                         result[i++] = rec.id;
28157                 });
28158                 return result;
28159         },
28160         
28161         isDirty: function() {
28162                 return this.isDirtyFlag;
28163         },
28164
28165 /**
28166  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28167  *      whole Element becomes the target, and this causes the drop gesture to append.
28168  */
28169     getTargetFromEvent : function(e) {
28170                 var target = e.getTarget();
28171                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28172                 target = target.parentNode;
28173                 }
28174                 if (!target) {
28175                         target = this.el.dom.lastChild || this.el.dom;
28176                 }
28177                 return target;
28178     },
28179
28180 /**
28181  *      Create the drag data which consists of an object which has the property "ddel" as
28182  *      the drag proxy element. 
28183  */
28184     getDragData : function(e) {
28185         var target = this.findItemFromChild(e.getTarget());
28186                 if(target) {
28187                         this.handleSelection(e);
28188                         var selNodes = this.getSelectedNodes();
28189             var dragData = {
28190                 source: this,
28191                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28192                 nodes: selNodes,
28193                 records: []
28194                         };
28195                         var selectedIndices = this.getSelectedIndexes();
28196                         for (var i = 0; i < selectedIndices.length; i++) {
28197                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28198                         }
28199                         if (selNodes.length == 1) {
28200                                 dragData.ddel = target.cloneNode(true); // the div element
28201                         } else {
28202                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28203                                 div.className = 'multi-proxy';
28204                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28205                                         div.appendChild(selNodes[i].cloneNode(true));
28206                                 }
28207                                 dragData.ddel = div;
28208                         }
28209             //console.log(dragData)
28210             //console.log(dragData.ddel.innerHTML)
28211                         return dragData;
28212                 }
28213         //console.log('nodragData')
28214                 return false;
28215     },
28216     
28217 /**     Specify to which ddGroup items in this DDView may be dragged. */
28218     setDraggable: function(ddGroup) {
28219         if (ddGroup instanceof Array) {
28220                 Roo.each(ddGroup, this.setDraggable, this);
28221                 return;
28222         }
28223         if (this.dragZone) {
28224                 this.dragZone.addToGroup(ddGroup);
28225         } else {
28226                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28227                                 containerScroll: true,
28228                                 ddGroup: ddGroup 
28229
28230                         });
28231 //                      Draggability implies selection. DragZone's mousedown selects the element.
28232                         if (!this.multiSelect) { this.singleSelect = true; }
28233
28234 //                      Wire the DragZone's handlers up to methods in *this*
28235                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28236                 }
28237     },
28238
28239 /**     Specify from which ddGroup this DDView accepts drops. */
28240     setDroppable: function(ddGroup) {
28241         if (ddGroup instanceof Array) {
28242                 Roo.each(ddGroup, this.setDroppable, this);
28243                 return;
28244         }
28245         if (this.dropZone) {
28246                 this.dropZone.addToGroup(ddGroup);
28247         } else {
28248                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28249                                 containerScroll: true,
28250                                 ddGroup: ddGroup
28251                         });
28252
28253 //                      Wire the DropZone's handlers up to methods in *this*
28254                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28255                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28256                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28257                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28258                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28259                 }
28260     },
28261
28262 /**     Decide whether to drop above or below a View node. */
28263     getDropPoint : function(e, n, dd){
28264         if (n == this.el.dom) { return "above"; }
28265                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28266                 var c = t + (b - t) / 2;
28267                 var y = Roo.lib.Event.getPageY(e);
28268                 if(y <= c) {
28269                         return "above";
28270                 }else{
28271                         return "below";
28272                 }
28273     },
28274
28275     onNodeEnter : function(n, dd, e, data){
28276                 return false;
28277     },
28278     
28279     onNodeOver : function(n, dd, e, data){
28280                 var pt = this.getDropPoint(e, n, dd);
28281                 // set the insert point style on the target node
28282                 var dragElClass = this.dropNotAllowed;
28283                 if (pt) {
28284                         var targetElClass;
28285                         if (pt == "above"){
28286                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28287                                 targetElClass = "x-view-drag-insert-above";
28288                         } else {
28289                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28290                                 targetElClass = "x-view-drag-insert-below";
28291                         }
28292                         if (this.lastInsertClass != targetElClass){
28293                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28294                                 this.lastInsertClass = targetElClass;
28295                         }
28296                 }
28297                 return dragElClass;
28298         },
28299
28300     onNodeOut : function(n, dd, e, data){
28301                 this.removeDropIndicators(n);
28302     },
28303
28304     onNodeDrop : function(n, dd, e, data){
28305         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28306                 return false;
28307         }
28308         var pt = this.getDropPoint(e, n, dd);
28309                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28310                 if (pt == "below") { insertAt++; }
28311                 for (var i = 0; i < data.records.length; i++) {
28312                         var r = data.records[i];
28313                         var dup = this.store.getById(r.id);
28314                         if (dup && (dd != this.dragZone)) {
28315                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28316                         } else {
28317                                 if (data.copy) {
28318                                         this.store.insert(insertAt++, r.copy());
28319                                 } else {
28320                                         data.source.isDirtyFlag = true;
28321                                         r.store.remove(r);
28322                                         this.store.insert(insertAt++, r);
28323                                 }
28324                                 this.isDirtyFlag = true;
28325                         }
28326                 }
28327                 this.dragZone.cachedTarget = null;
28328                 return true;
28329     },
28330
28331     removeDropIndicators : function(n){
28332                 if(n){
28333                         Roo.fly(n).removeClass([
28334                                 "x-view-drag-insert-above",
28335                                 "x-view-drag-insert-below"]);
28336                         this.lastInsertClass = "_noclass";
28337                 }
28338     },
28339
28340 /**
28341  *      Utility method. Add a delete option to the DDView's context menu.
28342  *      @param {String} imageUrl The URL of the "delete" icon image.
28343  */
28344         setDeletable: function(imageUrl) {
28345                 if (!this.singleSelect && !this.multiSelect) {
28346                         this.singleSelect = true;
28347                 }
28348                 var c = this.getContextMenu();
28349                 this.contextMenu.on("itemclick", function(item) {
28350                         switch (item.id) {
28351                                 case "delete":
28352                                         this.remove(this.getSelectedIndexes());
28353                                         break;
28354                         }
28355                 }, this);
28356                 this.contextMenu.add({
28357                         icon: imageUrl,
28358                         id: "delete",
28359                         text: 'Delete'
28360                 });
28361         },
28362         
28363 /**     Return the context menu for this DDView. */
28364         getContextMenu: function() {
28365                 if (!this.contextMenu) {
28366 //                      Create the View's context menu
28367                         this.contextMenu = new Roo.menu.Menu({
28368                                 id: this.id + "-contextmenu"
28369                         });
28370                         this.el.on("contextmenu", this.showContextMenu, this);
28371                 }
28372                 return this.contextMenu;
28373         },
28374         
28375         disableContextMenu: function() {
28376                 if (this.contextMenu) {
28377                         this.el.un("contextmenu", this.showContextMenu, this);
28378                 }
28379         },
28380
28381         showContextMenu: function(e, item) {
28382         item = this.findItemFromChild(e.getTarget());
28383                 if (item) {
28384                         e.stopEvent();
28385                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28386                         this.contextMenu.showAt(e.getXY());
28387             }
28388     },
28389
28390 /**
28391  *      Remove {@link Roo.data.Record}s at the specified indices.
28392  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28393  */
28394     remove: function(selectedIndices) {
28395                 selectedIndices = [].concat(selectedIndices);
28396                 for (var i = 0; i < selectedIndices.length; i++) {
28397                         var rec = this.store.getAt(selectedIndices[i]);
28398                         this.store.remove(rec);
28399                 }
28400     },
28401
28402 /**
28403  *      Double click fires the event, but also, if this is draggable, and there is only one other
28404  *      related DropZone, it transfers the selected node.
28405  */
28406     onDblClick : function(e){
28407         var item = this.findItemFromChild(e.getTarget());
28408         if(item){
28409             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28410                 return false;
28411             }
28412             if (this.dragGroup) {
28413                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28414                     while (targets.indexOf(this.dropZone) > -1) {
28415                             targets.remove(this.dropZone);
28416                                 }
28417                     if (targets.length == 1) {
28418                                         this.dragZone.cachedTarget = null;
28419                         var el = Roo.get(targets[0].getEl());
28420                         var box = el.getBox(true);
28421                         targets[0].onNodeDrop(el.dom, {
28422                                 target: el.dom,
28423                                 xy: [box.x, box.y + box.height - 1]
28424                         }, null, this.getDragData(e));
28425                     }
28426                 }
28427         }
28428     },
28429     
28430     handleSelection: function(e) {
28431                 this.dragZone.cachedTarget = null;
28432         var item = this.findItemFromChild(e.getTarget());
28433         if (!item) {
28434                 this.clearSelections(true);
28435                 return;
28436         }
28437                 if (item && (this.multiSelect || this.singleSelect)){
28438                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28439                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28440                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28441                                 this.unselect(item);
28442                         } else {
28443                                 this.select(item, this.multiSelect && e.ctrlKey);
28444                                 this.lastSelection = item;
28445                         }
28446                 }
28447     },
28448
28449     onItemClick : function(item, index, e){
28450                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28451                         return false;
28452                 }
28453                 return true;
28454     },
28455
28456     unselect : function(nodeInfo, suppressEvent){
28457                 var node = this.getNode(nodeInfo);
28458                 if(node && this.isSelected(node)){
28459                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28460                                 Roo.fly(node).removeClass(this.selectedClass);
28461                                 this.selections.remove(node);
28462                                 if(!suppressEvent){
28463                                         this.fireEvent("selectionchange", this, this.selections);
28464                                 }
28465                         }
28466                 }
28467     }
28468 });
28469 /*
28470  * Based on:
28471  * Ext JS Library 1.1.1
28472  * Copyright(c) 2006-2007, Ext JS, LLC.
28473  *
28474  * Originally Released Under LGPL - original licence link has changed is not relivant.
28475  *
28476  * Fork - LGPL
28477  * <script type="text/javascript">
28478  */
28479  
28480 /**
28481  * @class Roo.LayoutManager
28482  * @extends Roo.util.Observable
28483  * Base class for layout managers.
28484  */
28485 Roo.LayoutManager = function(container, config){
28486     Roo.LayoutManager.superclass.constructor.call(this);
28487     this.el = Roo.get(container);
28488     // ie scrollbar fix
28489     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28490         document.body.scroll = "no";
28491     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28492         this.el.position('relative');
28493     }
28494     this.id = this.el.id;
28495     this.el.addClass("x-layout-container");
28496     /** false to disable window resize monitoring @type Boolean */
28497     this.monitorWindowResize = true;
28498     this.regions = {};
28499     this.addEvents({
28500         /**
28501          * @event layout
28502          * Fires when a layout is performed. 
28503          * @param {Roo.LayoutManager} this
28504          */
28505         "layout" : true,
28506         /**
28507          * @event regionresized
28508          * Fires when the user resizes a region. 
28509          * @param {Roo.LayoutRegion} region The resized region
28510          * @param {Number} newSize The new size (width for east/west, height for north/south)
28511          */
28512         "regionresized" : true,
28513         /**
28514          * @event regioncollapsed
28515          * Fires when a region is collapsed. 
28516          * @param {Roo.LayoutRegion} region The collapsed region
28517          */
28518         "regioncollapsed" : true,
28519         /**
28520          * @event regionexpanded
28521          * Fires when a region is expanded.  
28522          * @param {Roo.LayoutRegion} region The expanded region
28523          */
28524         "regionexpanded" : true
28525     });
28526     this.updating = false;
28527     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28528 };
28529
28530 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28531     /**
28532      * Returns true if this layout is currently being updated
28533      * @return {Boolean}
28534      */
28535     isUpdating : function(){
28536         return this.updating; 
28537     },
28538     
28539     /**
28540      * Suspend the LayoutManager from doing auto-layouts while
28541      * making multiple add or remove calls
28542      */
28543     beginUpdate : function(){
28544         this.updating = true;    
28545     },
28546     
28547     /**
28548      * Restore auto-layouts and optionally disable the manager from performing a layout
28549      * @param {Boolean} noLayout true to disable a layout update 
28550      */
28551     endUpdate : function(noLayout){
28552         this.updating = false;
28553         if(!noLayout){
28554             this.layout();
28555         }    
28556     },
28557     
28558     layout: function(){
28559         
28560     },
28561     
28562     onRegionResized : function(region, newSize){
28563         this.fireEvent("regionresized", region, newSize);
28564         this.layout();
28565     },
28566     
28567     onRegionCollapsed : function(region){
28568         this.fireEvent("regioncollapsed", region);
28569     },
28570     
28571     onRegionExpanded : function(region){
28572         this.fireEvent("regionexpanded", region);
28573     },
28574         
28575     /**
28576      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28577      * performs box-model adjustments.
28578      * @return {Object} The size as an object {width: (the width), height: (the height)}
28579      */
28580     getViewSize : function(){
28581         var size;
28582         if(this.el.dom != document.body){
28583             size = this.el.getSize();
28584         }else{
28585             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28586         }
28587         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28588         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28589         return size;
28590     },
28591     
28592     /**
28593      * Returns the Element this layout is bound to.
28594      * @return {Roo.Element}
28595      */
28596     getEl : function(){
28597         return this.el;
28598     },
28599     
28600     /**
28601      * Returns the specified region.
28602      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28603      * @return {Roo.LayoutRegion}
28604      */
28605     getRegion : function(target){
28606         return this.regions[target.toLowerCase()];
28607     },
28608     
28609     onWindowResize : function(){
28610         if(this.monitorWindowResize){
28611             this.layout();
28612         }
28613     }
28614 });/*
28615  * Based on:
28616  * Ext JS Library 1.1.1
28617  * Copyright(c) 2006-2007, Ext JS, LLC.
28618  *
28619  * Originally Released Under LGPL - original licence link has changed is not relivant.
28620  *
28621  * Fork - LGPL
28622  * <script type="text/javascript">
28623  */
28624 /**
28625  * @class Roo.BorderLayout
28626  * @extends Roo.LayoutManager
28627  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28628  * please see: <br><br>
28629  * <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>
28630  * <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>
28631  * Example:
28632  <pre><code>
28633  var layout = new Roo.BorderLayout(document.body, {
28634     north: {
28635         initialSize: 25,
28636         titlebar: false
28637     },
28638     west: {
28639         split:true,
28640         initialSize: 200,
28641         minSize: 175,
28642         maxSize: 400,
28643         titlebar: true,
28644         collapsible: true
28645     },
28646     east: {
28647         split:true,
28648         initialSize: 202,
28649         minSize: 175,
28650         maxSize: 400,
28651         titlebar: true,
28652         collapsible: true
28653     },
28654     south: {
28655         split:true,
28656         initialSize: 100,
28657         minSize: 100,
28658         maxSize: 200,
28659         titlebar: true,
28660         collapsible: true
28661     },
28662     center: {
28663         titlebar: true,
28664         autoScroll:true,
28665         resizeTabs: true,
28666         minTabWidth: 50,
28667         preferredTabWidth: 150
28668     }
28669 });
28670
28671 // shorthand
28672 var CP = Roo.ContentPanel;
28673
28674 layout.beginUpdate();
28675 layout.add("north", new CP("north", "North"));
28676 layout.add("south", new CP("south", {title: "South", closable: true}));
28677 layout.add("west", new CP("west", {title: "West"}));
28678 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28679 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28680 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28681 layout.getRegion("center").showPanel("center1");
28682 layout.endUpdate();
28683 </code></pre>
28684
28685 <b>The container the layout is rendered into can be either the body element or any other element.
28686 If it is not the body element, the container needs to either be an absolute positioned element,
28687 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28688 the container size if it is not the body element.</b>
28689
28690 * @constructor
28691 * Create a new BorderLayout
28692 * @param {String/HTMLElement/Element} container The container this layout is bound to
28693 * @param {Object} config Configuration options
28694  */
28695 Roo.BorderLayout = function(container, config){
28696     config = config || {};
28697     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28698     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28699     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28700         var target = this.factory.validRegions[i];
28701         if(config[target]){
28702             this.addRegion(target, config[target]);
28703         }
28704     }
28705 };
28706
28707 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28708     /**
28709      * Creates and adds a new region if it doesn't already exist.
28710      * @param {String} target The target region key (north, south, east, west or center).
28711      * @param {Object} config The regions config object
28712      * @return {BorderLayoutRegion} The new region
28713      */
28714     addRegion : function(target, config){
28715         if(!this.regions[target]){
28716             var r = this.factory.create(target, this, config);
28717             this.bindRegion(target, r);
28718         }
28719         return this.regions[target];
28720     },
28721
28722     // private (kinda)
28723     bindRegion : function(name, r){
28724         this.regions[name] = r;
28725         r.on("visibilitychange", this.layout, this);
28726         r.on("paneladded", this.layout, this);
28727         r.on("panelremoved", this.layout, this);
28728         r.on("invalidated", this.layout, this);
28729         r.on("resized", this.onRegionResized, this);
28730         r.on("collapsed", this.onRegionCollapsed, this);
28731         r.on("expanded", this.onRegionExpanded, this);
28732     },
28733
28734     /**
28735      * Performs a layout update.
28736      */
28737     layout : function(){
28738         if(this.updating) {
28739             return;
28740         }
28741         var size = this.getViewSize();
28742         var w = size.width;
28743         var h = size.height;
28744         var centerW = w;
28745         var centerH = h;
28746         var centerY = 0;
28747         var centerX = 0;
28748         //var x = 0, y = 0;
28749
28750         var rs = this.regions;
28751         var north = rs["north"];
28752         var south = rs["south"]; 
28753         var west = rs["west"];
28754         var east = rs["east"];
28755         var center = rs["center"];
28756         //if(this.hideOnLayout){ // not supported anymore
28757             //c.el.setStyle("display", "none");
28758         //}
28759         if(north && north.isVisible()){
28760             var b = north.getBox();
28761             var m = north.getMargins();
28762             b.width = w - (m.left+m.right);
28763             b.x = m.left;
28764             b.y = m.top;
28765             centerY = b.height + b.y + m.bottom;
28766             centerH -= centerY;
28767             north.updateBox(this.safeBox(b));
28768         }
28769         if(south && south.isVisible()){
28770             var b = south.getBox();
28771             var m = south.getMargins();
28772             b.width = w - (m.left+m.right);
28773             b.x = m.left;
28774             var totalHeight = (b.height + m.top + m.bottom);
28775             b.y = h - totalHeight + m.top;
28776             centerH -= totalHeight;
28777             south.updateBox(this.safeBox(b));
28778         }
28779         if(west && west.isVisible()){
28780             var b = west.getBox();
28781             var m = west.getMargins();
28782             b.height = centerH - (m.top+m.bottom);
28783             b.x = m.left;
28784             b.y = centerY + m.top;
28785             var totalWidth = (b.width + m.left + m.right);
28786             centerX += totalWidth;
28787             centerW -= totalWidth;
28788             west.updateBox(this.safeBox(b));
28789         }
28790         if(east && east.isVisible()){
28791             var b = east.getBox();
28792             var m = east.getMargins();
28793             b.height = centerH - (m.top+m.bottom);
28794             var totalWidth = (b.width + m.left + m.right);
28795             b.x = w - totalWidth + m.left;
28796             b.y = centerY + m.top;
28797             centerW -= totalWidth;
28798             east.updateBox(this.safeBox(b));
28799         }
28800         if(center){
28801             var m = center.getMargins();
28802             var centerBox = {
28803                 x: centerX + m.left,
28804                 y: centerY + m.top,
28805                 width: centerW - (m.left+m.right),
28806                 height: centerH - (m.top+m.bottom)
28807             };
28808             //if(this.hideOnLayout){
28809                 //center.el.setStyle("display", "block");
28810             //}
28811             center.updateBox(this.safeBox(centerBox));
28812         }
28813         this.el.repaint();
28814         this.fireEvent("layout", this);
28815     },
28816
28817     // private
28818     safeBox : function(box){
28819         box.width = Math.max(0, box.width);
28820         box.height = Math.max(0, box.height);
28821         return box;
28822     },
28823
28824     /**
28825      * Adds a ContentPanel (or subclass) to this layout.
28826      * @param {String} target The target region key (north, south, east, west or center).
28827      * @param {Roo.ContentPanel} panel The panel to add
28828      * @return {Roo.ContentPanel} The added panel
28829      */
28830     add : function(target, panel){
28831          
28832         target = target.toLowerCase();
28833         return this.regions[target].add(panel);
28834     },
28835
28836     /**
28837      * Remove a ContentPanel (or subclass) to this layout.
28838      * @param {String} target The target region key (north, south, east, west or center).
28839      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28840      * @return {Roo.ContentPanel} The removed panel
28841      */
28842     remove : function(target, panel){
28843         target = target.toLowerCase();
28844         return this.regions[target].remove(panel);
28845     },
28846
28847     /**
28848      * Searches all regions for a panel with the specified id
28849      * @param {String} panelId
28850      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28851      */
28852     findPanel : function(panelId){
28853         var rs = this.regions;
28854         for(var target in rs){
28855             if(typeof rs[target] != "function"){
28856                 var p = rs[target].getPanel(panelId);
28857                 if(p){
28858                     return p;
28859                 }
28860             }
28861         }
28862         return null;
28863     },
28864
28865     /**
28866      * Searches all regions for a panel with the specified id and activates (shows) it.
28867      * @param {String/ContentPanel} panelId The panels id or the panel itself
28868      * @return {Roo.ContentPanel} The shown panel or null
28869      */
28870     showPanel : function(panelId) {
28871       var rs = this.regions;
28872       for(var target in rs){
28873          var r = rs[target];
28874          if(typeof r != "function"){
28875             if(r.hasPanel(panelId)){
28876                return r.showPanel(panelId);
28877             }
28878          }
28879       }
28880       return null;
28881    },
28882
28883    /**
28884      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28885      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28886      */
28887     restoreState : function(provider){
28888         if(!provider){
28889             provider = Roo.state.Manager;
28890         }
28891         var sm = new Roo.LayoutStateManager();
28892         sm.init(this, provider);
28893     },
28894
28895     /**
28896      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28897      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28898      * a valid ContentPanel config object.  Example:
28899      * <pre><code>
28900 // Create the main layout
28901 var layout = new Roo.BorderLayout('main-ct', {
28902     west: {
28903         split:true,
28904         minSize: 175,
28905         titlebar: true
28906     },
28907     center: {
28908         title:'Components'
28909     }
28910 }, 'main-ct');
28911
28912 // Create and add multiple ContentPanels at once via configs
28913 layout.batchAdd({
28914    west: {
28915        id: 'source-files',
28916        autoCreate:true,
28917        title:'Ext Source Files',
28918        autoScroll:true,
28919        fitToFrame:true
28920    },
28921    center : {
28922        el: cview,
28923        autoScroll:true,
28924        fitToFrame:true,
28925        toolbar: tb,
28926        resizeEl:'cbody'
28927    }
28928 });
28929 </code></pre>
28930      * @param {Object} regions An object containing ContentPanel configs by region name
28931      */
28932     batchAdd : function(regions){
28933         this.beginUpdate();
28934         for(var rname in regions){
28935             var lr = this.regions[rname];
28936             if(lr){
28937                 this.addTypedPanels(lr, regions[rname]);
28938             }
28939         }
28940         this.endUpdate();
28941     },
28942
28943     // private
28944     addTypedPanels : function(lr, ps){
28945         if(typeof ps == 'string'){
28946             lr.add(new Roo.ContentPanel(ps));
28947         }
28948         else if(ps instanceof Array){
28949             for(var i =0, len = ps.length; i < len; i++){
28950                 this.addTypedPanels(lr, ps[i]);
28951             }
28952         }
28953         else if(!ps.events){ // raw config?
28954             var el = ps.el;
28955             delete ps.el; // prevent conflict
28956             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28957         }
28958         else {  // panel object assumed!
28959             lr.add(ps);
28960         }
28961     },
28962     /**
28963      * Adds a xtype elements to the layout.
28964      * <pre><code>
28965
28966 layout.addxtype({
28967        xtype : 'ContentPanel',
28968        region: 'west',
28969        items: [ .... ]
28970    }
28971 );
28972
28973 layout.addxtype({
28974         xtype : 'NestedLayoutPanel',
28975         region: 'west',
28976         layout: {
28977            center: { },
28978            west: { }   
28979         },
28980         items : [ ... list of content panels or nested layout panels.. ]
28981    }
28982 );
28983 </code></pre>
28984      * @param {Object} cfg Xtype definition of item to add.
28985      */
28986     addxtype : function(cfg)
28987     {
28988         // basically accepts a pannel...
28989         // can accept a layout region..!?!?
28990         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28991         
28992         if (!cfg.xtype.match(/Panel$/)) {
28993             return false;
28994         }
28995         var ret = false;
28996         
28997         if (typeof(cfg.region) == 'undefined') {
28998             Roo.log("Failed to add Panel, region was not set");
28999             Roo.log(cfg);
29000             return false;
29001         }
29002         var region = cfg.region;
29003         delete cfg.region;
29004         
29005           
29006         var xitems = [];
29007         if (cfg.items) {
29008             xitems = cfg.items;
29009             delete cfg.items;
29010         }
29011         var nb = false;
29012         
29013         switch(cfg.xtype) 
29014         {
29015             case 'ContentPanel':  // ContentPanel (el, cfg)
29016             case 'ScrollPanel':  // ContentPanel (el, cfg)
29017             case 'ViewPanel': 
29018                 if(cfg.autoCreate) {
29019                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29020                 } else {
29021                     var el = this.el.createChild();
29022                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29023                 }
29024                 
29025                 this.add(region, ret);
29026                 break;
29027             
29028             
29029             case 'TreePanel': // our new panel!
29030                 cfg.el = this.el.createChild();
29031                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29032                 this.add(region, ret);
29033                 break;
29034             
29035             case 'NestedLayoutPanel': 
29036                 // create a new Layout (which is  a Border Layout...
29037                 var el = this.el.createChild();
29038                 var clayout = cfg.layout;
29039                 delete cfg.layout;
29040                 clayout.items   = clayout.items  || [];
29041                 // replace this exitems with the clayout ones..
29042                 xitems = clayout.items;
29043                  
29044                 
29045                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29046                     cfg.background = false;
29047                 }
29048                 var layout = new Roo.BorderLayout(el, clayout);
29049                 
29050                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29051                 //console.log('adding nested layout panel '  + cfg.toSource());
29052                 this.add(region, ret);
29053                 nb = {}; /// find first...
29054                 break;
29055                 
29056             case 'GridPanel': 
29057             
29058                 // needs grid and region
29059                 
29060                 //var el = this.getRegion(region).el.createChild();
29061                 var el = this.el.createChild();
29062                 // create the grid first...
29063                 
29064                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29065                 delete cfg.grid;
29066                 if (region == 'center' && this.active ) {
29067                     cfg.background = false;
29068                 }
29069                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29070                 
29071                 this.add(region, ret);
29072                 if (cfg.background) {
29073                     ret.on('activate', function(gp) {
29074                         if (!gp.grid.rendered) {
29075                             gp.grid.render();
29076                         }
29077                     });
29078                 } else {
29079                     grid.render();
29080                 }
29081                 break;
29082            
29083            
29084            
29085                 
29086                 
29087                 
29088             default:
29089                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29090                     
29091                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29092                     this.add(region, ret);
29093                 } else {
29094                 
29095                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29096                     return null;
29097                 }
29098                 
29099              // GridPanel (grid, cfg)
29100             
29101         }
29102         this.beginUpdate();
29103         // add children..
29104         var region = '';
29105         var abn = {};
29106         Roo.each(xitems, function(i)  {
29107             region = nb && i.region ? i.region : false;
29108             
29109             var add = ret.addxtype(i);
29110            
29111             if (region) {
29112                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29113                 if (!i.background) {
29114                     abn[region] = nb[region] ;
29115                 }
29116             }
29117             
29118         });
29119         this.endUpdate();
29120
29121         // make the last non-background panel active..
29122         //if (nb) { Roo.log(abn); }
29123         if (nb) {
29124             
29125             for(var r in abn) {
29126                 region = this.getRegion(r);
29127                 if (region) {
29128                     // tried using nb[r], but it does not work..
29129                      
29130                     region.showPanel(abn[r]);
29131                    
29132                 }
29133             }
29134         }
29135         return ret;
29136         
29137     }
29138 });
29139
29140 /**
29141  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29142  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29143  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29144  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29145  * <pre><code>
29146 // shorthand
29147 var CP = Roo.ContentPanel;
29148
29149 var layout = Roo.BorderLayout.create({
29150     north: {
29151         initialSize: 25,
29152         titlebar: false,
29153         panels: [new CP("north", "North")]
29154     },
29155     west: {
29156         split:true,
29157         initialSize: 200,
29158         minSize: 175,
29159         maxSize: 400,
29160         titlebar: true,
29161         collapsible: true,
29162         panels: [new CP("west", {title: "West"})]
29163     },
29164     east: {
29165         split:true,
29166         initialSize: 202,
29167         minSize: 175,
29168         maxSize: 400,
29169         titlebar: true,
29170         collapsible: true,
29171         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29172     },
29173     south: {
29174         split:true,
29175         initialSize: 100,
29176         minSize: 100,
29177         maxSize: 200,
29178         titlebar: true,
29179         collapsible: true,
29180         panels: [new CP("south", {title: "South", closable: true})]
29181     },
29182     center: {
29183         titlebar: true,
29184         autoScroll:true,
29185         resizeTabs: true,
29186         minTabWidth: 50,
29187         preferredTabWidth: 150,
29188         panels: [
29189             new CP("center1", {title: "Close Me", closable: true}),
29190             new CP("center2", {title: "Center Panel", closable: false})
29191         ]
29192     }
29193 }, document.body);
29194
29195 layout.getRegion("center").showPanel("center1");
29196 </code></pre>
29197  * @param config
29198  * @param targetEl
29199  */
29200 Roo.BorderLayout.create = function(config, targetEl){
29201     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29202     layout.beginUpdate();
29203     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29204     for(var j = 0, jlen = regions.length; j < jlen; j++){
29205         var lr = regions[j];
29206         if(layout.regions[lr] && config[lr].panels){
29207             var r = layout.regions[lr];
29208             var ps = config[lr].panels;
29209             layout.addTypedPanels(r, ps);
29210         }
29211     }
29212     layout.endUpdate();
29213     return layout;
29214 };
29215
29216 // private
29217 Roo.BorderLayout.RegionFactory = {
29218     // private
29219     validRegions : ["north","south","east","west","center"],
29220
29221     // private
29222     create : function(target, mgr, config){
29223         target = target.toLowerCase();
29224         if(config.lightweight || config.basic){
29225             return new Roo.BasicLayoutRegion(mgr, config, target);
29226         }
29227         switch(target){
29228             case "north":
29229                 return new Roo.NorthLayoutRegion(mgr, config);
29230             case "south":
29231                 return new Roo.SouthLayoutRegion(mgr, config);
29232             case "east":
29233                 return new Roo.EastLayoutRegion(mgr, config);
29234             case "west":
29235                 return new Roo.WestLayoutRegion(mgr, config);
29236             case "center":
29237                 return new Roo.CenterLayoutRegion(mgr, config);
29238         }
29239         throw 'Layout region "'+target+'" not supported.';
29240     }
29241 };/*
29242  * Based on:
29243  * Ext JS Library 1.1.1
29244  * Copyright(c) 2006-2007, Ext JS, LLC.
29245  *
29246  * Originally Released Under LGPL - original licence link has changed is not relivant.
29247  *
29248  * Fork - LGPL
29249  * <script type="text/javascript">
29250  */
29251  
29252 /**
29253  * @class Roo.BasicLayoutRegion
29254  * @extends Roo.util.Observable
29255  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29256  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29257  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29258  */
29259 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29260     this.mgr = mgr;
29261     this.position  = pos;
29262     this.events = {
29263         /**
29264          * @scope Roo.BasicLayoutRegion
29265          */
29266         
29267         /**
29268          * @event beforeremove
29269          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29270          * @param {Roo.LayoutRegion} this
29271          * @param {Roo.ContentPanel} panel The panel
29272          * @param {Object} e The cancel event object
29273          */
29274         "beforeremove" : true,
29275         /**
29276          * @event invalidated
29277          * Fires when the layout for this region is changed.
29278          * @param {Roo.LayoutRegion} this
29279          */
29280         "invalidated" : true,
29281         /**
29282          * @event visibilitychange
29283          * Fires when this region is shown or hidden 
29284          * @param {Roo.LayoutRegion} this
29285          * @param {Boolean} visibility true or false
29286          */
29287         "visibilitychange" : true,
29288         /**
29289          * @event paneladded
29290          * Fires when a panel is added. 
29291          * @param {Roo.LayoutRegion} this
29292          * @param {Roo.ContentPanel} panel The panel
29293          */
29294         "paneladded" : true,
29295         /**
29296          * @event panelremoved
29297          * Fires when a panel is removed. 
29298          * @param {Roo.LayoutRegion} this
29299          * @param {Roo.ContentPanel} panel The panel
29300          */
29301         "panelremoved" : true,
29302         /**
29303          * @event beforecollapse
29304          * Fires when this region before collapse.
29305          * @param {Roo.LayoutRegion} this
29306          */
29307         "beforecollapse" : true,
29308         /**
29309          * @event collapsed
29310          * Fires when this region is collapsed.
29311          * @param {Roo.LayoutRegion} this
29312          */
29313         "collapsed" : true,
29314         /**
29315          * @event expanded
29316          * Fires when this region is expanded.
29317          * @param {Roo.LayoutRegion} this
29318          */
29319         "expanded" : true,
29320         /**
29321          * @event slideshow
29322          * Fires when this region is slid into view.
29323          * @param {Roo.LayoutRegion} this
29324          */
29325         "slideshow" : true,
29326         /**
29327          * @event slidehide
29328          * Fires when this region slides out of view. 
29329          * @param {Roo.LayoutRegion} this
29330          */
29331         "slidehide" : true,
29332         /**
29333          * @event panelactivated
29334          * Fires when a panel is activated. 
29335          * @param {Roo.LayoutRegion} this
29336          * @param {Roo.ContentPanel} panel The activated panel
29337          */
29338         "panelactivated" : true,
29339         /**
29340          * @event resized
29341          * Fires when the user resizes this region. 
29342          * @param {Roo.LayoutRegion} this
29343          * @param {Number} newSize The new size (width for east/west, height for north/south)
29344          */
29345         "resized" : true
29346     };
29347     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29348     this.panels = new Roo.util.MixedCollection();
29349     this.panels.getKey = this.getPanelId.createDelegate(this);
29350     this.box = null;
29351     this.activePanel = null;
29352     // ensure listeners are added...
29353     
29354     if (config.listeners || config.events) {
29355         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29356             listeners : config.listeners || {},
29357             events : config.events || {}
29358         });
29359     }
29360     
29361     if(skipConfig !== true){
29362         this.applyConfig(config);
29363     }
29364 };
29365
29366 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29367     getPanelId : function(p){
29368         return p.getId();
29369     },
29370     
29371     applyConfig : function(config){
29372         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29373         this.config = config;
29374         
29375     },
29376     
29377     /**
29378      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29379      * the width, for horizontal (north, south) the height.
29380      * @param {Number} newSize The new width or height
29381      */
29382     resizeTo : function(newSize){
29383         var el = this.el ? this.el :
29384                  (this.activePanel ? this.activePanel.getEl() : null);
29385         if(el){
29386             switch(this.position){
29387                 case "east":
29388                 case "west":
29389                     el.setWidth(newSize);
29390                     this.fireEvent("resized", this, newSize);
29391                 break;
29392                 case "north":
29393                 case "south":
29394                     el.setHeight(newSize);
29395                     this.fireEvent("resized", this, newSize);
29396                 break;                
29397             }
29398         }
29399     },
29400     
29401     getBox : function(){
29402         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29403     },
29404     
29405     getMargins : function(){
29406         return this.margins;
29407     },
29408     
29409     updateBox : function(box){
29410         this.box = box;
29411         var el = this.activePanel.getEl();
29412         el.dom.style.left = box.x + "px";
29413         el.dom.style.top = box.y + "px";
29414         this.activePanel.setSize(box.width, box.height);
29415     },
29416     
29417     /**
29418      * Returns the container element for this region.
29419      * @return {Roo.Element}
29420      */
29421     getEl : function(){
29422         return this.activePanel;
29423     },
29424     
29425     /**
29426      * Returns true if this region is currently visible.
29427      * @return {Boolean}
29428      */
29429     isVisible : function(){
29430         return this.activePanel ? true : false;
29431     },
29432     
29433     setActivePanel : function(panel){
29434         panel = this.getPanel(panel);
29435         if(this.activePanel && this.activePanel != panel){
29436             this.activePanel.setActiveState(false);
29437             this.activePanel.getEl().setLeftTop(-10000,-10000);
29438         }
29439         this.activePanel = panel;
29440         panel.setActiveState(true);
29441         if(this.box){
29442             panel.setSize(this.box.width, this.box.height);
29443         }
29444         this.fireEvent("panelactivated", this, panel);
29445         this.fireEvent("invalidated");
29446     },
29447     
29448     /**
29449      * Show the specified panel.
29450      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29451      * @return {Roo.ContentPanel} The shown panel or null
29452      */
29453     showPanel : function(panel){
29454         if(panel = this.getPanel(panel)){
29455             this.setActivePanel(panel);
29456         }
29457         return panel;
29458     },
29459     
29460     /**
29461      * Get the active panel for this region.
29462      * @return {Roo.ContentPanel} The active panel or null
29463      */
29464     getActivePanel : function(){
29465         return this.activePanel;
29466     },
29467     
29468     /**
29469      * Add the passed ContentPanel(s)
29470      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29471      * @return {Roo.ContentPanel} The panel added (if only one was added)
29472      */
29473     add : function(panel){
29474         if(arguments.length > 1){
29475             for(var i = 0, len = arguments.length; i < len; i++) {
29476                 this.add(arguments[i]);
29477             }
29478             return null;
29479         }
29480         if(this.hasPanel(panel)){
29481             this.showPanel(panel);
29482             return panel;
29483         }
29484         var el = panel.getEl();
29485         if(el.dom.parentNode != this.mgr.el.dom){
29486             this.mgr.el.dom.appendChild(el.dom);
29487         }
29488         if(panel.setRegion){
29489             panel.setRegion(this);
29490         }
29491         this.panels.add(panel);
29492         el.setStyle("position", "absolute");
29493         if(!panel.background){
29494             this.setActivePanel(panel);
29495             if(this.config.initialSize && this.panels.getCount()==1){
29496                 this.resizeTo(this.config.initialSize);
29497             }
29498         }
29499         this.fireEvent("paneladded", this, panel);
29500         return panel;
29501     },
29502     
29503     /**
29504      * Returns true if the panel is in this region.
29505      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29506      * @return {Boolean}
29507      */
29508     hasPanel : function(panel){
29509         if(typeof panel == "object"){ // must be panel obj
29510             panel = panel.getId();
29511         }
29512         return this.getPanel(panel) ? true : false;
29513     },
29514     
29515     /**
29516      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29517      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29518      * @param {Boolean} preservePanel Overrides the config preservePanel option
29519      * @return {Roo.ContentPanel} The panel that was removed
29520      */
29521     remove : function(panel, preservePanel){
29522         panel = this.getPanel(panel);
29523         if(!panel){
29524             return null;
29525         }
29526         var e = {};
29527         this.fireEvent("beforeremove", this, panel, e);
29528         if(e.cancel === true){
29529             return null;
29530         }
29531         var panelId = panel.getId();
29532         this.panels.removeKey(panelId);
29533         return panel;
29534     },
29535     
29536     /**
29537      * Returns the panel specified or null if it's not in this region.
29538      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29539      * @return {Roo.ContentPanel}
29540      */
29541     getPanel : function(id){
29542         if(typeof id == "object"){ // must be panel obj
29543             return id;
29544         }
29545         return this.panels.get(id);
29546     },
29547     
29548     /**
29549      * Returns this regions position (north/south/east/west/center).
29550      * @return {String} 
29551      */
29552     getPosition: function(){
29553         return this.position;    
29554     }
29555 });/*
29556  * Based on:
29557  * Ext JS Library 1.1.1
29558  * Copyright(c) 2006-2007, Ext JS, LLC.
29559  *
29560  * Originally Released Under LGPL - original licence link has changed is not relivant.
29561  *
29562  * Fork - LGPL
29563  * <script type="text/javascript">
29564  */
29565  
29566 /**
29567  * @class Roo.LayoutRegion
29568  * @extends Roo.BasicLayoutRegion
29569  * This class represents a region in a layout manager.
29570  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29571  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29572  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29573  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29574  * @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})
29575  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29576  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29577  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29578  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29579  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29580  * @cfg {String}    title           The title for the region (overrides panel titles)
29581  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29582  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29583  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29584  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29585  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29586  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29587  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29588  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29589  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29590  * @cfg {Boolean}   showPin         True to show a pin button
29591  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29592  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29593  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29594  * @cfg {Number}    width           For East/West panels
29595  * @cfg {Number}    height          For North/South panels
29596  * @cfg {Boolean}   split           To show the splitter
29597  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29598  */
29599 Roo.LayoutRegion = function(mgr, config, pos){
29600     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29601     var dh = Roo.DomHelper;
29602     /** This region's container element 
29603     * @type Roo.Element */
29604     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29605     /** This region's title element 
29606     * @type Roo.Element */
29607
29608     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29609         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29610         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29611     ]}, true);
29612     this.titleEl.enableDisplayMode();
29613     /** This region's title text element 
29614     * @type HTMLElement */
29615     this.titleTextEl = this.titleEl.dom.firstChild;
29616     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29617     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29618     this.closeBtn.enableDisplayMode();
29619     this.closeBtn.on("click", this.closeClicked, this);
29620     this.closeBtn.hide();
29621
29622     this.createBody(config);
29623     this.visible = true;
29624     this.collapsed = false;
29625
29626     if(config.hideWhenEmpty){
29627         this.hide();
29628         this.on("paneladded", this.validateVisibility, this);
29629         this.on("panelremoved", this.validateVisibility, this);
29630     }
29631     this.applyConfig(config);
29632 };
29633
29634 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29635
29636     createBody : function(){
29637         /** This region's body element 
29638         * @type Roo.Element */
29639         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29640     },
29641
29642     applyConfig : function(c){
29643         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29644             var dh = Roo.DomHelper;
29645             if(c.titlebar !== false){
29646                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29647                 this.collapseBtn.on("click", this.collapse, this);
29648                 this.collapseBtn.enableDisplayMode();
29649
29650                 if(c.showPin === true || this.showPin){
29651                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29652                     this.stickBtn.enableDisplayMode();
29653                     this.stickBtn.on("click", this.expand, this);
29654                     this.stickBtn.hide();
29655                 }
29656             }
29657             /** This region's collapsed element
29658             * @type Roo.Element */
29659             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29660                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29661             ]}, true);
29662             if(c.floatable !== false){
29663                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29664                this.collapsedEl.on("click", this.collapseClick, this);
29665             }
29666
29667             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29668                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29669                    id: "message", unselectable: "on", style:{"float":"left"}});
29670                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29671              }
29672             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29673             this.expandBtn.on("click", this.expand, this);
29674         }
29675         if(this.collapseBtn){
29676             this.collapseBtn.setVisible(c.collapsible == true);
29677         }
29678         this.cmargins = c.cmargins || this.cmargins ||
29679                          (this.position == "west" || this.position == "east" ?
29680                              {top: 0, left: 2, right:2, bottom: 0} :
29681                              {top: 2, left: 0, right:0, bottom: 2});
29682         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29683         this.bottomTabs = c.tabPosition != "top";
29684         this.autoScroll = c.autoScroll || false;
29685         if(this.autoScroll){
29686             this.bodyEl.setStyle("overflow", "auto");
29687         }else{
29688             this.bodyEl.setStyle("overflow", "hidden");
29689         }
29690         //if(c.titlebar !== false){
29691             if((!c.titlebar && !c.title) || c.titlebar === false){
29692                 this.titleEl.hide();
29693             }else{
29694                 this.titleEl.show();
29695                 if(c.title){
29696                     this.titleTextEl.innerHTML = c.title;
29697                 }
29698             }
29699         //}
29700         this.duration = c.duration || .30;
29701         this.slideDuration = c.slideDuration || .45;
29702         this.config = c;
29703         if(c.collapsed){
29704             this.collapse(true);
29705         }
29706         if(c.hidden){
29707             this.hide();
29708         }
29709     },
29710     /**
29711      * Returns true if this region is currently visible.
29712      * @return {Boolean}
29713      */
29714     isVisible : function(){
29715         return this.visible;
29716     },
29717
29718     /**
29719      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29720      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29721      */
29722     setCollapsedTitle : function(title){
29723         title = title || "&#160;";
29724         if(this.collapsedTitleTextEl){
29725             this.collapsedTitleTextEl.innerHTML = title;
29726         }
29727     },
29728
29729     getBox : function(){
29730         var b;
29731         if(!this.collapsed){
29732             b = this.el.getBox(false, true);
29733         }else{
29734             b = this.collapsedEl.getBox(false, true);
29735         }
29736         return b;
29737     },
29738
29739     getMargins : function(){
29740         return this.collapsed ? this.cmargins : this.margins;
29741     },
29742
29743     highlight : function(){
29744         this.el.addClass("x-layout-panel-dragover");
29745     },
29746
29747     unhighlight : function(){
29748         this.el.removeClass("x-layout-panel-dragover");
29749     },
29750
29751     updateBox : function(box){
29752         this.box = box;
29753         if(!this.collapsed){
29754             this.el.dom.style.left = box.x + "px";
29755             this.el.dom.style.top = box.y + "px";
29756             this.updateBody(box.width, box.height);
29757         }else{
29758             this.collapsedEl.dom.style.left = box.x + "px";
29759             this.collapsedEl.dom.style.top = box.y + "px";
29760             this.collapsedEl.setSize(box.width, box.height);
29761         }
29762         if(this.tabs){
29763             this.tabs.autoSizeTabs();
29764         }
29765     },
29766
29767     updateBody : function(w, h){
29768         if(w !== null){
29769             this.el.setWidth(w);
29770             w -= this.el.getBorderWidth("rl");
29771             if(this.config.adjustments){
29772                 w += this.config.adjustments[0];
29773             }
29774         }
29775         if(h !== null){
29776             this.el.setHeight(h);
29777             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29778             h -= this.el.getBorderWidth("tb");
29779             if(this.config.adjustments){
29780                 h += this.config.adjustments[1];
29781             }
29782             this.bodyEl.setHeight(h);
29783             if(this.tabs){
29784                 h = this.tabs.syncHeight(h);
29785             }
29786         }
29787         if(this.panelSize){
29788             w = w !== null ? w : this.panelSize.width;
29789             h = h !== null ? h : this.panelSize.height;
29790         }
29791         if(this.activePanel){
29792             var el = this.activePanel.getEl();
29793             w = w !== null ? w : el.getWidth();
29794             h = h !== null ? h : el.getHeight();
29795             this.panelSize = {width: w, height: h};
29796             this.activePanel.setSize(w, h);
29797         }
29798         if(Roo.isIE && this.tabs){
29799             this.tabs.el.repaint();
29800         }
29801     },
29802
29803     /**
29804      * Returns the container element for this region.
29805      * @return {Roo.Element}
29806      */
29807     getEl : function(){
29808         return this.el;
29809     },
29810
29811     /**
29812      * Hides this region.
29813      */
29814     hide : function(){
29815         if(!this.collapsed){
29816             this.el.dom.style.left = "-2000px";
29817             this.el.hide();
29818         }else{
29819             this.collapsedEl.dom.style.left = "-2000px";
29820             this.collapsedEl.hide();
29821         }
29822         this.visible = false;
29823         this.fireEvent("visibilitychange", this, false);
29824     },
29825
29826     /**
29827      * Shows this region if it was previously hidden.
29828      */
29829     show : function(){
29830         Roo.log('show??');
29831         if(!this.collapsed){
29832             this.el.show();
29833         }else{
29834             this.collapsedEl.show();
29835         }
29836         this.visible = true;
29837         this.fireEvent("visibilitychange", this, true);
29838     },
29839
29840     closeClicked : function(){
29841         if(this.activePanel){
29842             this.remove(this.activePanel);
29843         }
29844     },
29845
29846     collapseClick : function(e){
29847         if(this.isSlid){
29848            e.stopPropagation();
29849            this.slideIn();
29850         }else{
29851            e.stopPropagation();
29852            this.slideOut();
29853         }
29854     },
29855
29856     /**
29857      * Collapses this region.
29858      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29859      */
29860     collapse : function(skipAnim, skipCheck = false){
29861         if(this.collapsed) {
29862             return;
29863         }
29864         
29865         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29866             
29867             this.collapsed = true;
29868             if(this.split){
29869                 this.split.el.hide();
29870             }
29871             if(this.config.animate && skipAnim !== true){
29872                 this.fireEvent("invalidated", this);
29873                 this.animateCollapse();
29874             }else{
29875                 this.el.setLocation(-20000,-20000);
29876                 this.el.hide();
29877                 this.collapsedEl.show();
29878                 this.fireEvent("collapsed", this);
29879                 this.fireEvent("invalidated", this);
29880             }
29881         }
29882         
29883     },
29884
29885     animateCollapse : function(){
29886         // overridden
29887     },
29888
29889     /**
29890      * Expands this region if it was previously collapsed.
29891      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29892      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29893      */
29894     expand : function(e, skipAnim){
29895         if(e) {
29896             e.stopPropagation();
29897         }
29898         if(!this.collapsed || this.el.hasActiveFx()) {
29899             return;
29900         }
29901         if(this.isSlid){
29902             this.afterSlideIn();
29903             skipAnim = true;
29904         }
29905         this.collapsed = false;
29906         if(this.config.animate && skipAnim !== true){
29907             this.animateExpand();
29908         }else{
29909             this.el.show();
29910             if(this.split){
29911                 this.split.el.show();
29912             }
29913             this.collapsedEl.setLocation(-2000,-2000);
29914             this.collapsedEl.hide();
29915             this.fireEvent("invalidated", this);
29916             this.fireEvent("expanded", this);
29917         }
29918     },
29919
29920     animateExpand : function(){
29921         // overridden
29922     },
29923
29924     initTabs : function()
29925     {
29926         this.bodyEl.setStyle("overflow", "hidden");
29927         var ts = new Roo.TabPanel(
29928                 this.bodyEl.dom,
29929                 {
29930                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29931                     disableTooltips: this.config.disableTabTips,
29932                     toolbar : this.config.toolbar
29933                 }
29934         );
29935         if(this.config.hideTabs){
29936             ts.stripWrap.setDisplayed(false);
29937         }
29938         this.tabs = ts;
29939         ts.resizeTabs = this.config.resizeTabs === true;
29940         ts.minTabWidth = this.config.minTabWidth || 40;
29941         ts.maxTabWidth = this.config.maxTabWidth || 250;
29942         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29943         ts.monitorResize = false;
29944         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29945         ts.bodyEl.addClass('x-layout-tabs-body');
29946         this.panels.each(this.initPanelAsTab, this);
29947     },
29948
29949     initPanelAsTab : function(panel){
29950         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29951                     this.config.closeOnTab && panel.isClosable());
29952         if(panel.tabTip !== undefined){
29953             ti.setTooltip(panel.tabTip);
29954         }
29955         ti.on("activate", function(){
29956               this.setActivePanel(panel);
29957         }, this);
29958         if(this.config.closeOnTab){
29959             ti.on("beforeclose", function(t, e){
29960                 e.cancel = true;
29961                 this.remove(panel);
29962             }, this);
29963         }
29964         return ti;
29965     },
29966
29967     updatePanelTitle : function(panel, title){
29968         if(this.activePanel == panel){
29969             this.updateTitle(title);
29970         }
29971         if(this.tabs){
29972             var ti = this.tabs.getTab(panel.getEl().id);
29973             ti.setText(title);
29974             if(panel.tabTip !== undefined){
29975                 ti.setTooltip(panel.tabTip);
29976             }
29977         }
29978     },
29979
29980     updateTitle : function(title){
29981         if(this.titleTextEl && !this.config.title){
29982             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29983         }
29984     },
29985
29986     setActivePanel : function(panel){
29987         panel = this.getPanel(panel);
29988         if(this.activePanel && this.activePanel != panel){
29989             this.activePanel.setActiveState(false);
29990         }
29991         this.activePanel = panel;
29992         panel.setActiveState(true);
29993         if(this.panelSize){
29994             panel.setSize(this.panelSize.width, this.panelSize.height);
29995         }
29996         if(this.closeBtn){
29997             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29998         }
29999         this.updateTitle(panel.getTitle());
30000         if(this.tabs){
30001             this.fireEvent("invalidated", this);
30002         }
30003         this.fireEvent("panelactivated", this, panel);
30004     },
30005
30006     /**
30007      * Shows the specified panel.
30008      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30009      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30010      */
30011     showPanel : function(panel)
30012     {
30013         panel = this.getPanel(panel);
30014         if(panel){
30015             if(this.tabs){
30016                 var tab = this.tabs.getTab(panel.getEl().id);
30017                 if(tab.isHidden()){
30018                     this.tabs.unhideTab(tab.id);
30019                 }
30020                 tab.activate();
30021             }else{
30022                 this.setActivePanel(panel);
30023             }
30024         }
30025         return panel;
30026     },
30027
30028     /**
30029      * Get the active panel for this region.
30030      * @return {Roo.ContentPanel} The active panel or null
30031      */
30032     getActivePanel : function(){
30033         return this.activePanel;
30034     },
30035
30036     validateVisibility : function(){
30037         if(this.panels.getCount() < 1){
30038             this.updateTitle("&#160;");
30039             this.closeBtn.hide();
30040             this.hide();
30041         }else{
30042             if(!this.isVisible()){
30043                 this.show();
30044             }
30045         }
30046     },
30047
30048     /**
30049      * Adds the passed ContentPanel(s) to this region.
30050      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30051      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30052      */
30053     add : function(panel){
30054         if(arguments.length > 1){
30055             for(var i = 0, len = arguments.length; i < len; i++) {
30056                 this.add(arguments[i]);
30057             }
30058             return null;
30059         }
30060         if(this.hasPanel(panel)){
30061             this.showPanel(panel);
30062             return panel;
30063         }
30064         panel.setRegion(this);
30065         this.panels.add(panel);
30066         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30067             this.bodyEl.dom.appendChild(panel.getEl().dom);
30068             if(panel.background !== true){
30069                 this.setActivePanel(panel);
30070             }
30071             this.fireEvent("paneladded", this, panel);
30072             return panel;
30073         }
30074         if(!this.tabs){
30075             this.initTabs();
30076         }else{
30077             this.initPanelAsTab(panel);
30078         }
30079         if(panel.background !== true){
30080             this.tabs.activate(panel.getEl().id);
30081         }
30082         this.fireEvent("paneladded", this, panel);
30083         return panel;
30084     },
30085
30086     /**
30087      * Hides the tab for the specified panel.
30088      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30089      */
30090     hidePanel : function(panel){
30091         if(this.tabs && (panel = this.getPanel(panel))){
30092             this.tabs.hideTab(panel.getEl().id);
30093         }
30094     },
30095
30096     /**
30097      * Unhides the tab for a previously hidden panel.
30098      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30099      */
30100     unhidePanel : function(panel){
30101         if(this.tabs && (panel = this.getPanel(panel))){
30102             this.tabs.unhideTab(panel.getEl().id);
30103         }
30104     },
30105
30106     clearPanels : function(){
30107         while(this.panels.getCount() > 0){
30108              this.remove(this.panels.first());
30109         }
30110     },
30111
30112     /**
30113      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30114      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30115      * @param {Boolean} preservePanel Overrides the config preservePanel option
30116      * @return {Roo.ContentPanel} The panel that was removed
30117      */
30118     remove : function(panel, preservePanel){
30119         panel = this.getPanel(panel);
30120         if(!panel){
30121             return null;
30122         }
30123         var e = {};
30124         this.fireEvent("beforeremove", this, panel, e);
30125         if(e.cancel === true){
30126             return null;
30127         }
30128         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30129         var panelId = panel.getId();
30130         this.panels.removeKey(panelId);
30131         if(preservePanel){
30132             document.body.appendChild(panel.getEl().dom);
30133         }
30134         if(this.tabs){
30135             this.tabs.removeTab(panel.getEl().id);
30136         }else if (!preservePanel){
30137             this.bodyEl.dom.removeChild(panel.getEl().dom);
30138         }
30139         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30140             var p = this.panels.first();
30141             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30142             tempEl.appendChild(p.getEl().dom);
30143             this.bodyEl.update("");
30144             this.bodyEl.dom.appendChild(p.getEl().dom);
30145             tempEl = null;
30146             this.updateTitle(p.getTitle());
30147             this.tabs = null;
30148             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30149             this.setActivePanel(p);
30150         }
30151         panel.setRegion(null);
30152         if(this.activePanel == panel){
30153             this.activePanel = null;
30154         }
30155         if(this.config.autoDestroy !== false && preservePanel !== true){
30156             try{panel.destroy();}catch(e){}
30157         }
30158         this.fireEvent("panelremoved", this, panel);
30159         return panel;
30160     },
30161
30162     /**
30163      * Returns the TabPanel component used by this region
30164      * @return {Roo.TabPanel}
30165      */
30166     getTabs : function(){
30167         return this.tabs;
30168     },
30169
30170     createTool : function(parentEl, className){
30171         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30172             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30173         btn.addClassOnOver("x-layout-tools-button-over");
30174         return btn;
30175     }
30176 });/*
30177  * Based on:
30178  * Ext JS Library 1.1.1
30179  * Copyright(c) 2006-2007, Ext JS, LLC.
30180  *
30181  * Originally Released Under LGPL - original licence link has changed is not relivant.
30182  *
30183  * Fork - LGPL
30184  * <script type="text/javascript">
30185  */
30186  
30187
30188
30189 /**
30190  * @class Roo.SplitLayoutRegion
30191  * @extends Roo.LayoutRegion
30192  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30193  */
30194 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30195     this.cursor = cursor;
30196     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30197 };
30198
30199 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30200     splitTip : "Drag to resize.",
30201     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30202     useSplitTips : false,
30203
30204     applyConfig : function(config){
30205         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30206         if(config.split){
30207             if(!this.split){
30208                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30209                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30210                 /** The SplitBar for this region 
30211                 * @type Roo.SplitBar */
30212                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30213                 this.split.on("moved", this.onSplitMove, this);
30214                 this.split.useShim = config.useShim === true;
30215                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30216                 if(this.useSplitTips){
30217                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30218                 }
30219                 if(config.collapsible){
30220                     this.split.el.on("dblclick", this.collapse,  this);
30221                 }
30222             }
30223             if(typeof config.minSize != "undefined"){
30224                 this.split.minSize = config.minSize;
30225             }
30226             if(typeof config.maxSize != "undefined"){
30227                 this.split.maxSize = config.maxSize;
30228             }
30229             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30230                 this.hideSplitter();
30231             }
30232         }
30233     },
30234
30235     getHMaxSize : function(){
30236          var cmax = this.config.maxSize || 10000;
30237          var center = this.mgr.getRegion("center");
30238          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30239     },
30240
30241     getVMaxSize : function(){
30242          var cmax = this.config.maxSize || 10000;
30243          var center = this.mgr.getRegion("center");
30244          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30245     },
30246
30247     onSplitMove : function(split, newSize){
30248         this.fireEvent("resized", this, newSize);
30249     },
30250     
30251     /** 
30252      * Returns the {@link Roo.SplitBar} for this region.
30253      * @return {Roo.SplitBar}
30254      */
30255     getSplitBar : function(){
30256         return this.split;
30257     },
30258     
30259     hide : function(){
30260         this.hideSplitter();
30261         Roo.SplitLayoutRegion.superclass.hide.call(this);
30262     },
30263
30264     hideSplitter : function(){
30265         if(this.split){
30266             this.split.el.setLocation(-2000,-2000);
30267             this.split.el.hide();
30268         }
30269     },
30270
30271     show : function(){
30272         if(this.split){
30273             this.split.el.show();
30274         }
30275         Roo.SplitLayoutRegion.superclass.show.call(this);
30276     },
30277     
30278     beforeSlide: function(){
30279         if(Roo.isGecko){// firefox overflow auto bug workaround
30280             this.bodyEl.clip();
30281             if(this.tabs) {
30282                 this.tabs.bodyEl.clip();
30283             }
30284             if(this.activePanel){
30285                 this.activePanel.getEl().clip();
30286                 
30287                 if(this.activePanel.beforeSlide){
30288                     this.activePanel.beforeSlide();
30289                 }
30290             }
30291         }
30292     },
30293     
30294     afterSlide : function(){
30295         if(Roo.isGecko){// firefox overflow auto bug workaround
30296             this.bodyEl.unclip();
30297             if(this.tabs) {
30298                 this.tabs.bodyEl.unclip();
30299             }
30300             if(this.activePanel){
30301                 this.activePanel.getEl().unclip();
30302                 if(this.activePanel.afterSlide){
30303                     this.activePanel.afterSlide();
30304                 }
30305             }
30306         }
30307     },
30308
30309     initAutoHide : function(){
30310         if(this.autoHide !== false){
30311             if(!this.autoHideHd){
30312                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30313                 this.autoHideHd = {
30314                     "mouseout": function(e){
30315                         if(!e.within(this.el, true)){
30316                             st.delay(500);
30317                         }
30318                     },
30319                     "mouseover" : function(e){
30320                         st.cancel();
30321                     },
30322                     scope : this
30323                 };
30324             }
30325             this.el.on(this.autoHideHd);
30326         }
30327     },
30328
30329     clearAutoHide : function(){
30330         if(this.autoHide !== false){
30331             this.el.un("mouseout", this.autoHideHd.mouseout);
30332             this.el.un("mouseover", this.autoHideHd.mouseover);
30333         }
30334     },
30335
30336     clearMonitor : function(){
30337         Roo.get(document).un("click", this.slideInIf, this);
30338     },
30339
30340     // these names are backwards but not changed for compat
30341     slideOut : function(){
30342         if(this.isSlid || this.el.hasActiveFx()){
30343             return;
30344         }
30345         this.isSlid = true;
30346         if(this.collapseBtn){
30347             this.collapseBtn.hide();
30348         }
30349         this.closeBtnState = this.closeBtn.getStyle('display');
30350         this.closeBtn.hide();
30351         if(this.stickBtn){
30352             this.stickBtn.show();
30353         }
30354         this.el.show();
30355         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30356         this.beforeSlide();
30357         this.el.setStyle("z-index", 10001);
30358         this.el.slideIn(this.getSlideAnchor(), {
30359             callback: function(){
30360                 this.afterSlide();
30361                 this.initAutoHide();
30362                 Roo.get(document).on("click", this.slideInIf, this);
30363                 this.fireEvent("slideshow", this);
30364             },
30365             scope: this,
30366             block: true
30367         });
30368     },
30369
30370     afterSlideIn : function(){
30371         this.clearAutoHide();
30372         this.isSlid = false;
30373         this.clearMonitor();
30374         this.el.setStyle("z-index", "");
30375         if(this.collapseBtn){
30376             this.collapseBtn.show();
30377         }
30378         this.closeBtn.setStyle('display', this.closeBtnState);
30379         if(this.stickBtn){
30380             this.stickBtn.hide();
30381         }
30382         this.fireEvent("slidehide", this);
30383     },
30384
30385     slideIn : function(cb){
30386         if(!this.isSlid || this.el.hasActiveFx()){
30387             Roo.callback(cb);
30388             return;
30389         }
30390         this.isSlid = false;
30391         this.beforeSlide();
30392         this.el.slideOut(this.getSlideAnchor(), {
30393             callback: function(){
30394                 this.el.setLeftTop(-10000, -10000);
30395                 this.afterSlide();
30396                 this.afterSlideIn();
30397                 Roo.callback(cb);
30398             },
30399             scope: this,
30400             block: true
30401         });
30402     },
30403     
30404     slideInIf : function(e){
30405         if(!e.within(this.el)){
30406             this.slideIn();
30407         }
30408     },
30409
30410     animateCollapse : function(){
30411         this.beforeSlide();
30412         this.el.setStyle("z-index", 20000);
30413         var anchor = this.getSlideAnchor();
30414         this.el.slideOut(anchor, {
30415             callback : function(){
30416                 this.el.setStyle("z-index", "");
30417                 this.collapsedEl.slideIn(anchor, {duration:.3});
30418                 this.afterSlide();
30419                 this.el.setLocation(-10000,-10000);
30420                 this.el.hide();
30421                 this.fireEvent("collapsed", this);
30422             },
30423             scope: this,
30424             block: true
30425         });
30426     },
30427
30428     animateExpand : function(){
30429         this.beforeSlide();
30430         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30431         this.el.setStyle("z-index", 20000);
30432         this.collapsedEl.hide({
30433             duration:.1
30434         });
30435         this.el.slideIn(this.getSlideAnchor(), {
30436             callback : function(){
30437                 this.el.setStyle("z-index", "");
30438                 this.afterSlide();
30439                 if(this.split){
30440                     this.split.el.show();
30441                 }
30442                 this.fireEvent("invalidated", this);
30443                 this.fireEvent("expanded", this);
30444             },
30445             scope: this,
30446             block: true
30447         });
30448     },
30449
30450     anchors : {
30451         "west" : "left",
30452         "east" : "right",
30453         "north" : "top",
30454         "south" : "bottom"
30455     },
30456
30457     sanchors : {
30458         "west" : "l",
30459         "east" : "r",
30460         "north" : "t",
30461         "south" : "b"
30462     },
30463
30464     canchors : {
30465         "west" : "tl-tr",
30466         "east" : "tr-tl",
30467         "north" : "tl-bl",
30468         "south" : "bl-tl"
30469     },
30470
30471     getAnchor : function(){
30472         return this.anchors[this.position];
30473     },
30474
30475     getCollapseAnchor : function(){
30476         return this.canchors[this.position];
30477     },
30478
30479     getSlideAnchor : function(){
30480         return this.sanchors[this.position];
30481     },
30482
30483     getAlignAdj : function(){
30484         var cm = this.cmargins;
30485         switch(this.position){
30486             case "west":
30487                 return [0, 0];
30488             break;
30489             case "east":
30490                 return [0, 0];
30491             break;
30492             case "north":
30493                 return [0, 0];
30494             break;
30495             case "south":
30496                 return [0, 0];
30497             break;
30498         }
30499     },
30500
30501     getExpandAdj : function(){
30502         var c = this.collapsedEl, cm = this.cmargins;
30503         switch(this.position){
30504             case "west":
30505                 return [-(cm.right+c.getWidth()+cm.left), 0];
30506             break;
30507             case "east":
30508                 return [cm.right+c.getWidth()+cm.left, 0];
30509             break;
30510             case "north":
30511                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30512             break;
30513             case "south":
30514                 return [0, cm.top+cm.bottom+c.getHeight()];
30515             break;
30516         }
30517     }
30518 });/*
30519  * Based on:
30520  * Ext JS Library 1.1.1
30521  * Copyright(c) 2006-2007, Ext JS, LLC.
30522  *
30523  * Originally Released Under LGPL - original licence link has changed is not relivant.
30524  *
30525  * Fork - LGPL
30526  * <script type="text/javascript">
30527  */
30528 /*
30529  * These classes are private internal classes
30530  */
30531 Roo.CenterLayoutRegion = function(mgr, config){
30532     Roo.LayoutRegion.call(this, mgr, config, "center");
30533     this.visible = true;
30534     this.minWidth = config.minWidth || 20;
30535     this.minHeight = config.minHeight || 20;
30536 };
30537
30538 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30539     hide : function(){
30540         // center panel can't be hidden
30541     },
30542     
30543     show : function(){
30544         // center panel can't be hidden
30545     },
30546     
30547     getMinWidth: function(){
30548         return this.minWidth;
30549     },
30550     
30551     getMinHeight: function(){
30552         return this.minHeight;
30553     }
30554 });
30555
30556
30557 Roo.NorthLayoutRegion = function(mgr, config){
30558     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30559     if(this.split){
30560         this.split.placement = Roo.SplitBar.TOP;
30561         this.split.orientation = Roo.SplitBar.VERTICAL;
30562         this.split.el.addClass("x-layout-split-v");
30563     }
30564     var size = config.initialSize || config.height;
30565     if(typeof size != "undefined"){
30566         this.el.setHeight(size);
30567     }
30568 };
30569 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30570     orientation: Roo.SplitBar.VERTICAL,
30571     getBox : function(){
30572         if(this.collapsed){
30573             return this.collapsedEl.getBox();
30574         }
30575         var box = this.el.getBox();
30576         if(this.split){
30577             box.height += this.split.el.getHeight();
30578         }
30579         return box;
30580     },
30581     
30582     updateBox : function(box){
30583         if(this.split && !this.collapsed){
30584             box.height -= this.split.el.getHeight();
30585             this.split.el.setLeft(box.x);
30586             this.split.el.setTop(box.y+box.height);
30587             this.split.el.setWidth(box.width);
30588         }
30589         if(this.collapsed){
30590             this.updateBody(box.width, null);
30591         }
30592         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30593     }
30594 });
30595
30596 Roo.SouthLayoutRegion = function(mgr, config){
30597     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30598     if(this.split){
30599         this.split.placement = Roo.SplitBar.BOTTOM;
30600         this.split.orientation = Roo.SplitBar.VERTICAL;
30601         this.split.el.addClass("x-layout-split-v");
30602     }
30603     var size = config.initialSize || config.height;
30604     if(typeof size != "undefined"){
30605         this.el.setHeight(size);
30606     }
30607 };
30608 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30609     orientation: Roo.SplitBar.VERTICAL,
30610     getBox : function(){
30611         if(this.collapsed){
30612             return this.collapsedEl.getBox();
30613         }
30614         var box = this.el.getBox();
30615         if(this.split){
30616             var sh = this.split.el.getHeight();
30617             box.height += sh;
30618             box.y -= sh;
30619         }
30620         return box;
30621     },
30622     
30623     updateBox : function(box){
30624         if(this.split && !this.collapsed){
30625             var sh = this.split.el.getHeight();
30626             box.height -= sh;
30627             box.y += sh;
30628             this.split.el.setLeft(box.x);
30629             this.split.el.setTop(box.y-sh);
30630             this.split.el.setWidth(box.width);
30631         }
30632         if(this.collapsed){
30633             this.updateBody(box.width, null);
30634         }
30635         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30636     }
30637 });
30638
30639 Roo.EastLayoutRegion = function(mgr, config){
30640     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30641     if(this.split){
30642         this.split.placement = Roo.SplitBar.RIGHT;
30643         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30644         this.split.el.addClass("x-layout-split-h");
30645     }
30646     var size = config.initialSize || config.width;
30647     if(typeof size != "undefined"){
30648         this.el.setWidth(size);
30649     }
30650 };
30651 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30652     orientation: Roo.SplitBar.HORIZONTAL,
30653     getBox : function(){
30654         if(this.collapsed){
30655             return this.collapsedEl.getBox();
30656         }
30657         var box = this.el.getBox();
30658         if(this.split){
30659             var sw = this.split.el.getWidth();
30660             box.width += sw;
30661             box.x -= sw;
30662         }
30663         return box;
30664     },
30665
30666     updateBox : function(box){
30667         if(this.split && !this.collapsed){
30668             var sw = this.split.el.getWidth();
30669             box.width -= sw;
30670             this.split.el.setLeft(box.x);
30671             this.split.el.setTop(box.y);
30672             this.split.el.setHeight(box.height);
30673             box.x += sw;
30674         }
30675         if(this.collapsed){
30676             this.updateBody(null, box.height);
30677         }
30678         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30679     }
30680 });
30681
30682 Roo.WestLayoutRegion = function(mgr, config){
30683     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30684     if(this.split){
30685         this.split.placement = Roo.SplitBar.LEFT;
30686         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30687         this.split.el.addClass("x-layout-split-h");
30688     }
30689     var size = config.initialSize || config.width;
30690     if(typeof size != "undefined"){
30691         this.el.setWidth(size);
30692     }
30693 };
30694 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30695     orientation: Roo.SplitBar.HORIZONTAL,
30696     getBox : function(){
30697         if(this.collapsed){
30698             return this.collapsedEl.getBox();
30699         }
30700         var box = this.el.getBox();
30701         if(this.split){
30702             box.width += this.split.el.getWidth();
30703         }
30704         return box;
30705     },
30706     
30707     updateBox : function(box){
30708         if(this.split && !this.collapsed){
30709             var sw = this.split.el.getWidth();
30710             box.width -= sw;
30711             this.split.el.setLeft(box.x+box.width);
30712             this.split.el.setTop(box.y);
30713             this.split.el.setHeight(box.height);
30714         }
30715         if(this.collapsed){
30716             this.updateBody(null, box.height);
30717         }
30718         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30719     }
30720 });
30721 /*
30722  * Based on:
30723  * Ext JS Library 1.1.1
30724  * Copyright(c) 2006-2007, Ext JS, LLC.
30725  *
30726  * Originally Released Under LGPL - original licence link has changed is not relivant.
30727  *
30728  * Fork - LGPL
30729  * <script type="text/javascript">
30730  */
30731  
30732  
30733 /*
30734  * Private internal class for reading and applying state
30735  */
30736 Roo.LayoutStateManager = function(layout){
30737      // default empty state
30738      this.state = {
30739         north: {},
30740         south: {},
30741         east: {},
30742         west: {}       
30743     };
30744 };
30745
30746 Roo.LayoutStateManager.prototype = {
30747     init : function(layout, provider){
30748         this.provider = provider;
30749         var state = provider.get(layout.id+"-layout-state");
30750         if(state){
30751             var wasUpdating = layout.isUpdating();
30752             if(!wasUpdating){
30753                 layout.beginUpdate();
30754             }
30755             for(var key in state){
30756                 if(typeof state[key] != "function"){
30757                     var rstate = state[key];
30758                     var r = layout.getRegion(key);
30759                     if(r && rstate){
30760                         if(rstate.size){
30761                             r.resizeTo(rstate.size);
30762                         }
30763                         if(rstate.collapsed == true){
30764                             r.collapse(true);
30765                         }else{
30766                             r.expand(null, true);
30767                         }
30768                     }
30769                 }
30770             }
30771             if(!wasUpdating){
30772                 layout.endUpdate();
30773             }
30774             this.state = state; 
30775         }
30776         this.layout = layout;
30777         layout.on("regionresized", this.onRegionResized, this);
30778         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30779         layout.on("regionexpanded", this.onRegionExpanded, this);
30780     },
30781     
30782     storeState : function(){
30783         this.provider.set(this.layout.id+"-layout-state", this.state);
30784     },
30785     
30786     onRegionResized : function(region, newSize){
30787         this.state[region.getPosition()].size = newSize;
30788         this.storeState();
30789     },
30790     
30791     onRegionCollapsed : function(region){
30792         this.state[region.getPosition()].collapsed = true;
30793         this.storeState();
30794     },
30795     
30796     onRegionExpanded : function(region){
30797         this.state[region.getPosition()].collapsed = false;
30798         this.storeState();
30799     }
30800 };/*
30801  * Based on:
30802  * Ext JS Library 1.1.1
30803  * Copyright(c) 2006-2007, Ext JS, LLC.
30804  *
30805  * Originally Released Under LGPL - original licence link has changed is not relivant.
30806  *
30807  * Fork - LGPL
30808  * <script type="text/javascript">
30809  */
30810 /**
30811  * @class Roo.ContentPanel
30812  * @extends Roo.util.Observable
30813  * A basic ContentPanel element.
30814  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30815  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30816  * @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
30817  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30818  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30819  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30820  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30821  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30822  * @cfg {String} title          The title for this panel
30823  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30824  * @cfg {String} url            Calls {@link #setUrl} with this value
30825  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30826  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30827  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30828  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30829
30830  * @constructor
30831  * Create a new ContentPanel.
30832  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30833  * @param {String/Object} config A string to set only the title or a config object
30834  * @param {String} content (optional) Set the HTML content for this panel
30835  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30836  */
30837 Roo.ContentPanel = function(el, config, content){
30838     
30839      
30840     /*
30841     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30842         config = el;
30843         el = Roo.id();
30844     }
30845     if (config && config.parentLayout) { 
30846         el = config.parentLayout.el.createChild(); 
30847     }
30848     */
30849     if(el.autoCreate){ // xtype is available if this is called from factory
30850         config = el;
30851         el = Roo.id();
30852     }
30853     this.el = Roo.get(el);
30854     if(!this.el && config && config.autoCreate){
30855         if(typeof config.autoCreate == "object"){
30856             if(!config.autoCreate.id){
30857                 config.autoCreate.id = config.id||el;
30858             }
30859             this.el = Roo.DomHelper.append(document.body,
30860                         config.autoCreate, true);
30861         }else{
30862             this.el = Roo.DomHelper.append(document.body,
30863                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30864         }
30865     }
30866     this.closable = false;
30867     this.loaded = false;
30868     this.active = false;
30869     if(typeof config == "string"){
30870         this.title = config;
30871     }else{
30872         Roo.apply(this, config);
30873     }
30874     
30875     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30876         this.wrapEl = this.el.wrap();
30877         this.toolbar.container = this.el.insertSibling(false, 'before');
30878         this.toolbar = new Roo.Toolbar(this.toolbar);
30879     }
30880     
30881     // xtype created footer. - not sure if will work as we normally have to render first..
30882     if (this.footer && !this.footer.el && this.footer.xtype) {
30883         if (!this.wrapEl) {
30884             this.wrapEl = this.el.wrap();
30885         }
30886     
30887         this.footer.container = this.wrapEl.createChild();
30888          
30889         this.footer = Roo.factory(this.footer, Roo);
30890         
30891     }
30892     
30893     if(this.resizeEl){
30894         this.resizeEl = Roo.get(this.resizeEl, true);
30895     }else{
30896         this.resizeEl = this.el;
30897     }
30898     // handle view.xtype
30899     
30900  
30901     
30902     
30903     this.addEvents({
30904         /**
30905          * @event activate
30906          * Fires when this panel is activated. 
30907          * @param {Roo.ContentPanel} this
30908          */
30909         "activate" : true,
30910         /**
30911          * @event deactivate
30912          * Fires when this panel is activated. 
30913          * @param {Roo.ContentPanel} this
30914          */
30915         "deactivate" : true,
30916
30917         /**
30918          * @event resize
30919          * Fires when this panel is resized if fitToFrame is true.
30920          * @param {Roo.ContentPanel} this
30921          * @param {Number} width The width after any component adjustments
30922          * @param {Number} height The height after any component adjustments
30923          */
30924         "resize" : true,
30925         
30926          /**
30927          * @event render
30928          * Fires when this tab is created
30929          * @param {Roo.ContentPanel} this
30930          */
30931         "render" : true
30932          
30933         
30934     });
30935     
30936
30937     
30938     
30939     if(this.autoScroll){
30940         this.resizeEl.setStyle("overflow", "auto");
30941     } else {
30942         // fix randome scrolling
30943         this.el.on('scroll', function() {
30944             Roo.log('fix random scolling');
30945             this.scrollTo('top',0); 
30946         });
30947     }
30948     content = content || this.content;
30949     if(content){
30950         this.setContent(content);
30951     }
30952     if(config && config.url){
30953         this.setUrl(this.url, this.params, this.loadOnce);
30954     }
30955     
30956     
30957     
30958     Roo.ContentPanel.superclass.constructor.call(this);
30959     
30960     if (this.view && typeof(this.view.xtype) != 'undefined') {
30961         this.view.el = this.el.appendChild(document.createElement("div"));
30962         this.view = Roo.factory(this.view); 
30963         this.view.render  &&  this.view.render(false, '');  
30964     }
30965     
30966     
30967     this.fireEvent('render', this);
30968 };
30969
30970 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30971     tabTip:'',
30972     setRegion : function(region){
30973         this.region = region;
30974         if(region){
30975            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30976         }else{
30977            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30978         } 
30979     },
30980     
30981     /**
30982      * Returns the toolbar for this Panel if one was configured. 
30983      * @return {Roo.Toolbar} 
30984      */
30985     getToolbar : function(){
30986         return this.toolbar;
30987     },
30988     
30989     setActiveState : function(active){
30990         this.active = active;
30991         if(!active){
30992             this.fireEvent("deactivate", this);
30993         }else{
30994             this.fireEvent("activate", this);
30995         }
30996     },
30997     /**
30998      * Updates this panel's element
30999      * @param {String} content The new content
31000      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31001     */
31002     setContent : function(content, loadScripts){
31003         this.el.update(content, loadScripts);
31004     },
31005
31006     ignoreResize : function(w, h){
31007         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31008             return true;
31009         }else{
31010             this.lastSize = {width: w, height: h};
31011             return false;
31012         }
31013     },
31014     /**
31015      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31016      * @return {Roo.UpdateManager} The UpdateManager
31017      */
31018     getUpdateManager : function(){
31019         return this.el.getUpdateManager();
31020     },
31021      /**
31022      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31023      * @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:
31024 <pre><code>
31025 panel.load({
31026     url: "your-url.php",
31027     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31028     callback: yourFunction,
31029     scope: yourObject, //(optional scope)
31030     discardUrl: false,
31031     nocache: false,
31032     text: "Loading...",
31033     timeout: 30,
31034     scripts: false
31035 });
31036 </code></pre>
31037      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31038      * 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.
31039      * @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}
31040      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31041      * @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.
31042      * @return {Roo.ContentPanel} this
31043      */
31044     load : function(){
31045         var um = this.el.getUpdateManager();
31046         um.update.apply(um, arguments);
31047         return this;
31048     },
31049
31050
31051     /**
31052      * 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.
31053      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31054      * @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)
31055      * @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)
31056      * @return {Roo.UpdateManager} The UpdateManager
31057      */
31058     setUrl : function(url, params, loadOnce){
31059         if(this.refreshDelegate){
31060             this.removeListener("activate", this.refreshDelegate);
31061         }
31062         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31063         this.on("activate", this.refreshDelegate);
31064         return this.el.getUpdateManager();
31065     },
31066     
31067     _handleRefresh : function(url, params, loadOnce){
31068         if(!loadOnce || !this.loaded){
31069             var updater = this.el.getUpdateManager();
31070             updater.update(url, params, this._setLoaded.createDelegate(this));
31071         }
31072     },
31073     
31074     _setLoaded : function(){
31075         this.loaded = true;
31076     }, 
31077     
31078     /**
31079      * Returns this panel's id
31080      * @return {String} 
31081      */
31082     getId : function(){
31083         return this.el.id;
31084     },
31085     
31086     /** 
31087      * Returns this panel's element - used by regiosn to add.
31088      * @return {Roo.Element} 
31089      */
31090     getEl : function(){
31091         return this.wrapEl || this.el;
31092     },
31093     
31094     adjustForComponents : function(width, height)
31095     {
31096         //Roo.log('adjustForComponents ');
31097         if(this.resizeEl != this.el){
31098             width -= this.el.getFrameWidth('lr');
31099             height -= this.el.getFrameWidth('tb');
31100         }
31101         if(this.toolbar){
31102             var te = this.toolbar.getEl();
31103             height -= te.getHeight();
31104             te.setWidth(width);
31105         }
31106         if(this.footer){
31107             var te = this.footer.getEl();
31108             //Roo.log("footer:" + te.getHeight());
31109             
31110             height -= te.getHeight();
31111             te.setWidth(width);
31112         }
31113         
31114         
31115         if(this.adjustments){
31116             width += this.adjustments[0];
31117             height += this.adjustments[1];
31118         }
31119         return {"width": width, "height": height};
31120     },
31121     
31122     setSize : function(width, height){
31123         if(this.fitToFrame && !this.ignoreResize(width, height)){
31124             if(this.fitContainer && this.resizeEl != this.el){
31125                 this.el.setSize(width, height);
31126             }
31127             var size = this.adjustForComponents(width, height);
31128             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31129             this.fireEvent('resize', this, size.width, size.height);
31130         }
31131     },
31132     
31133     /**
31134      * Returns this panel's title
31135      * @return {String} 
31136      */
31137     getTitle : function(){
31138         return this.title;
31139     },
31140     
31141     /**
31142      * Set this panel's title
31143      * @param {String} title
31144      */
31145     setTitle : function(title){
31146         this.title = title;
31147         if(this.region){
31148             this.region.updatePanelTitle(this, title);
31149         }
31150     },
31151     
31152     /**
31153      * Returns true is this panel was configured to be closable
31154      * @return {Boolean} 
31155      */
31156     isClosable : function(){
31157         return this.closable;
31158     },
31159     
31160     beforeSlide : function(){
31161         this.el.clip();
31162         this.resizeEl.clip();
31163     },
31164     
31165     afterSlide : function(){
31166         this.el.unclip();
31167         this.resizeEl.unclip();
31168     },
31169     
31170     /**
31171      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31172      *   Will fail silently if the {@link #setUrl} method has not been called.
31173      *   This does not activate the panel, just updates its content.
31174      */
31175     refresh : function(){
31176         if(this.refreshDelegate){
31177            this.loaded = false;
31178            this.refreshDelegate();
31179         }
31180     },
31181     
31182     /**
31183      * Destroys this panel
31184      */
31185     destroy : function(){
31186         this.el.removeAllListeners();
31187         var tempEl = document.createElement("span");
31188         tempEl.appendChild(this.el.dom);
31189         tempEl.innerHTML = "";
31190         this.el.remove();
31191         this.el = null;
31192     },
31193     
31194     /**
31195      * form - if the content panel contains a form - this is a reference to it.
31196      * @type {Roo.form.Form}
31197      */
31198     form : false,
31199     /**
31200      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31201      *    This contains a reference to it.
31202      * @type {Roo.View}
31203      */
31204     view : false,
31205     
31206       /**
31207      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31208      * <pre><code>
31209
31210 layout.addxtype({
31211        xtype : 'Form',
31212        items: [ .... ]
31213    }
31214 );
31215
31216 </code></pre>
31217      * @param {Object} cfg Xtype definition of item to add.
31218      */
31219     
31220     addxtype : function(cfg) {
31221         // add form..
31222         if (cfg.xtype.match(/^Form$/)) {
31223             
31224             var el;
31225             //if (this.footer) {
31226             //    el = this.footer.container.insertSibling(false, 'before');
31227             //} else {
31228                 el = this.el.createChild();
31229             //}
31230
31231             this.form = new  Roo.form.Form(cfg);
31232             
31233             
31234             if ( this.form.allItems.length) {
31235                 this.form.render(el.dom);
31236             }
31237             return this.form;
31238         }
31239         // should only have one of theses..
31240         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31241             // views.. should not be just added - used named prop 'view''
31242             
31243             cfg.el = this.el.appendChild(document.createElement("div"));
31244             // factory?
31245             
31246             var ret = new Roo.factory(cfg);
31247              
31248              ret.render && ret.render(false, ''); // render blank..
31249             this.view = ret;
31250             return ret;
31251         }
31252         return false;
31253     }
31254 });
31255
31256 /**
31257  * @class Roo.GridPanel
31258  * @extends Roo.ContentPanel
31259  * @constructor
31260  * Create a new GridPanel.
31261  * @param {Roo.grid.Grid} grid The grid for this panel
31262  * @param {String/Object} config A string to set only the panel's title, or a config object
31263  */
31264 Roo.GridPanel = function(grid, config){
31265     
31266   
31267     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31268         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31269         
31270     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31271     
31272     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31273     
31274     if(this.toolbar){
31275         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31276     }
31277     // xtype created footer. - not sure if will work as we normally have to render first..
31278     if (this.footer && !this.footer.el && this.footer.xtype) {
31279         
31280         this.footer.container = this.grid.getView().getFooterPanel(true);
31281         this.footer.dataSource = this.grid.dataSource;
31282         this.footer = Roo.factory(this.footer, Roo);
31283         
31284     }
31285     
31286     grid.monitorWindowResize = false; // turn off autosizing
31287     grid.autoHeight = false;
31288     grid.autoWidth = false;
31289     this.grid = grid;
31290     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31291 };
31292
31293 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31294     getId : function(){
31295         return this.grid.id;
31296     },
31297     
31298     /**
31299      * Returns the grid for this panel
31300      * @return {Roo.grid.Grid} 
31301      */
31302     getGrid : function(){
31303         return this.grid;    
31304     },
31305     
31306     setSize : function(width, height){
31307         if(!this.ignoreResize(width, height)){
31308             var grid = this.grid;
31309             var size = this.adjustForComponents(width, height);
31310             grid.getGridEl().setSize(size.width, size.height);
31311             grid.autoSize();
31312         }
31313     },
31314     
31315     beforeSlide : function(){
31316         this.grid.getView().scroller.clip();
31317     },
31318     
31319     afterSlide : function(){
31320         this.grid.getView().scroller.unclip();
31321     },
31322     
31323     destroy : function(){
31324         this.grid.destroy();
31325         delete this.grid;
31326         Roo.GridPanel.superclass.destroy.call(this); 
31327     }
31328 });
31329
31330
31331 /**
31332  * @class Roo.NestedLayoutPanel
31333  * @extends Roo.ContentPanel
31334  * @constructor
31335  * Create a new NestedLayoutPanel.
31336  * 
31337  * 
31338  * @param {Roo.BorderLayout} layout The layout for this panel
31339  * @param {String/Object} config A string to set only the title or a config object
31340  */
31341 Roo.NestedLayoutPanel = function(layout, config)
31342 {
31343     // construct with only one argument..
31344     /* FIXME - implement nicer consturctors
31345     if (layout.layout) {
31346         config = layout;
31347         layout = config.layout;
31348         delete config.layout;
31349     }
31350     if (layout.xtype && !layout.getEl) {
31351         // then layout needs constructing..
31352         layout = Roo.factory(layout, Roo);
31353     }
31354     */
31355     
31356     
31357     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31358     
31359     layout.monitorWindowResize = false; // turn off autosizing
31360     this.layout = layout;
31361     this.layout.getEl().addClass("x-layout-nested-layout");
31362     
31363     
31364     
31365     
31366 };
31367
31368 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31369
31370     setSize : function(width, height){
31371         if(!this.ignoreResize(width, height)){
31372             var size = this.adjustForComponents(width, height);
31373             var el = this.layout.getEl();
31374             el.setSize(size.width, size.height);
31375             var touch = el.dom.offsetWidth;
31376             this.layout.layout();
31377             // ie requires a double layout on the first pass
31378             if(Roo.isIE && !this.initialized){
31379                 this.initialized = true;
31380                 this.layout.layout();
31381             }
31382         }
31383     },
31384     
31385     // activate all subpanels if not currently active..
31386     
31387     setActiveState : function(active){
31388         this.active = active;
31389         if(!active){
31390             this.fireEvent("deactivate", this);
31391             return;
31392         }
31393         
31394         this.fireEvent("activate", this);
31395         // not sure if this should happen before or after..
31396         if (!this.layout) {
31397             return; // should not happen..
31398         }
31399         var reg = false;
31400         for (var r in this.layout.regions) {
31401             reg = this.layout.getRegion(r);
31402             if (reg.getActivePanel()) {
31403                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31404                 reg.setActivePanel(reg.getActivePanel());
31405                 continue;
31406             }
31407             if (!reg.panels.length) {
31408                 continue;
31409             }
31410             reg.showPanel(reg.getPanel(0));
31411         }
31412         
31413         
31414         
31415         
31416     },
31417     
31418     /**
31419      * Returns the nested BorderLayout for this panel
31420      * @return {Roo.BorderLayout} 
31421      */
31422     getLayout : function(){
31423         return this.layout;
31424     },
31425     
31426      /**
31427      * Adds a xtype elements to the layout of the nested panel
31428      * <pre><code>
31429
31430 panel.addxtype({
31431        xtype : 'ContentPanel',
31432        region: 'west',
31433        items: [ .... ]
31434    }
31435 );
31436
31437 panel.addxtype({
31438         xtype : 'NestedLayoutPanel',
31439         region: 'west',
31440         layout: {
31441            center: { },
31442            west: { }   
31443         },
31444         items : [ ... list of content panels or nested layout panels.. ]
31445    }
31446 );
31447 </code></pre>
31448      * @param {Object} cfg Xtype definition of item to add.
31449      */
31450     addxtype : function(cfg) {
31451         return this.layout.addxtype(cfg);
31452     
31453     }
31454 });
31455
31456 Roo.ScrollPanel = function(el, config, content){
31457     config = config || {};
31458     config.fitToFrame = true;
31459     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31460     
31461     this.el.dom.style.overflow = "hidden";
31462     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31463     this.el.removeClass("x-layout-inactive-content");
31464     this.el.on("mousewheel", this.onWheel, this);
31465
31466     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31467     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31468     up.unselectable(); down.unselectable();
31469     up.on("click", this.scrollUp, this);
31470     down.on("click", this.scrollDown, this);
31471     up.addClassOnOver("x-scroller-btn-over");
31472     down.addClassOnOver("x-scroller-btn-over");
31473     up.addClassOnClick("x-scroller-btn-click");
31474     down.addClassOnClick("x-scroller-btn-click");
31475     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31476
31477     this.resizeEl = this.el;
31478     this.el = wrap; this.up = up; this.down = down;
31479 };
31480
31481 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31482     increment : 100,
31483     wheelIncrement : 5,
31484     scrollUp : function(){
31485         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31486     },
31487
31488     scrollDown : function(){
31489         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31490     },
31491
31492     afterScroll : function(){
31493         var el = this.resizeEl;
31494         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31495         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31496         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31497     },
31498
31499     setSize : function(){
31500         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31501         this.afterScroll();
31502     },
31503
31504     onWheel : function(e){
31505         var d = e.getWheelDelta();
31506         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31507         this.afterScroll();
31508         e.stopEvent();
31509     },
31510
31511     setContent : function(content, loadScripts){
31512         this.resizeEl.update(content, loadScripts);
31513     }
31514
31515 });
31516
31517
31518
31519
31520
31521
31522
31523
31524
31525 /**
31526  * @class Roo.TreePanel
31527  * @extends Roo.ContentPanel
31528  * @constructor
31529  * Create a new TreePanel. - defaults to fit/scoll contents.
31530  * @param {String/Object} config A string to set only the panel's title, or a config object
31531  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31532  */
31533 Roo.TreePanel = function(config){
31534     var el = config.el;
31535     var tree = config.tree;
31536     delete config.tree; 
31537     delete config.el; // hopefull!
31538     
31539     // wrapper for IE7 strict & safari scroll issue
31540     
31541     var treeEl = el.createChild();
31542     config.resizeEl = treeEl;
31543     
31544     
31545     
31546     Roo.TreePanel.superclass.constructor.call(this, el, config);
31547  
31548  
31549     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31550     //console.log(tree);
31551     this.on('activate', function()
31552     {
31553         if (this.tree.rendered) {
31554             return;
31555         }
31556         //console.log('render tree');
31557         this.tree.render();
31558     });
31559     // this should not be needed.. - it's actually the 'el' that resizes?
31560     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31561     
31562     //this.on('resize',  function (cp, w, h) {
31563     //        this.tree.innerCt.setWidth(w);
31564     //        this.tree.innerCt.setHeight(h);
31565     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31566     //});
31567
31568         
31569     
31570 };
31571
31572 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31573     fitToFrame : true,
31574     autoScroll : true
31575 });
31576
31577
31578
31579
31580
31581
31582
31583
31584
31585
31586
31587 /*
31588  * Based on:
31589  * Ext JS Library 1.1.1
31590  * Copyright(c) 2006-2007, Ext JS, LLC.
31591  *
31592  * Originally Released Under LGPL - original licence link has changed is not relivant.
31593  *
31594  * Fork - LGPL
31595  * <script type="text/javascript">
31596  */
31597  
31598
31599 /**
31600  * @class Roo.ReaderLayout
31601  * @extends Roo.BorderLayout
31602  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31603  * center region containing two nested regions (a top one for a list view and one for item preview below),
31604  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31605  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31606  * expedites the setup of the overall layout and regions for this common application style.
31607  * Example:
31608  <pre><code>
31609 var reader = new Roo.ReaderLayout();
31610 var CP = Roo.ContentPanel;  // shortcut for adding
31611
31612 reader.beginUpdate();
31613 reader.add("north", new CP("north", "North"));
31614 reader.add("west", new CP("west", {title: "West"}));
31615 reader.add("east", new CP("east", {title: "East"}));
31616
31617 reader.regions.listView.add(new CP("listView", "List"));
31618 reader.regions.preview.add(new CP("preview", "Preview"));
31619 reader.endUpdate();
31620 </code></pre>
31621 * @constructor
31622 * Create a new ReaderLayout
31623 * @param {Object} config Configuration options
31624 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31625 * document.body if omitted)
31626 */
31627 Roo.ReaderLayout = function(config, renderTo){
31628     var c = config || {size:{}};
31629     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31630         north: c.north !== false ? Roo.apply({
31631             split:false,
31632             initialSize: 32,
31633             titlebar: false
31634         }, c.north) : false,
31635         west: c.west !== false ? Roo.apply({
31636             split:true,
31637             initialSize: 200,
31638             minSize: 175,
31639             maxSize: 400,
31640             titlebar: true,
31641             collapsible: true,
31642             animate: true,
31643             margins:{left:5,right:0,bottom:5,top:5},
31644             cmargins:{left:5,right:5,bottom:5,top:5}
31645         }, c.west) : false,
31646         east: c.east !== false ? Roo.apply({
31647             split:true,
31648             initialSize: 200,
31649             minSize: 175,
31650             maxSize: 400,
31651             titlebar: true,
31652             collapsible: true,
31653             animate: true,
31654             margins:{left:0,right:5,bottom:5,top:5},
31655             cmargins:{left:5,right:5,bottom:5,top:5}
31656         }, c.east) : false,
31657         center: Roo.apply({
31658             tabPosition: 'top',
31659             autoScroll:false,
31660             closeOnTab: true,
31661             titlebar:false,
31662             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31663         }, c.center)
31664     });
31665
31666     this.el.addClass('x-reader');
31667
31668     this.beginUpdate();
31669
31670     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31671         south: c.preview !== false ? Roo.apply({
31672             split:true,
31673             initialSize: 200,
31674             minSize: 100,
31675             autoScroll:true,
31676             collapsible:true,
31677             titlebar: true,
31678             cmargins:{top:5,left:0, right:0, bottom:0}
31679         }, c.preview) : false,
31680         center: Roo.apply({
31681             autoScroll:false,
31682             titlebar:false,
31683             minHeight:200
31684         }, c.listView)
31685     });
31686     this.add('center', new Roo.NestedLayoutPanel(inner,
31687             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31688
31689     this.endUpdate();
31690
31691     this.regions.preview = inner.getRegion('south');
31692     this.regions.listView = inner.getRegion('center');
31693 };
31694
31695 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31696  * Based on:
31697  * Ext JS Library 1.1.1
31698  * Copyright(c) 2006-2007, Ext JS, LLC.
31699  *
31700  * Originally Released Under LGPL - original licence link has changed is not relivant.
31701  *
31702  * Fork - LGPL
31703  * <script type="text/javascript">
31704  */
31705  
31706 /**
31707  * @class Roo.grid.Grid
31708  * @extends Roo.util.Observable
31709  * This class represents the primary interface of a component based grid control.
31710  * <br><br>Usage:<pre><code>
31711  var grid = new Roo.grid.Grid("my-container-id", {
31712      ds: myDataStore,
31713      cm: myColModel,
31714      selModel: mySelectionModel,
31715      autoSizeColumns: true,
31716      monitorWindowResize: false,
31717      trackMouseOver: true
31718  });
31719  // set any options
31720  grid.render();
31721  * </code></pre>
31722  * <b>Common Problems:</b><br/>
31723  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31724  * element will correct this<br/>
31725  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31726  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31727  * are unpredictable.<br/>
31728  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31729  * grid to calculate dimensions/offsets.<br/>
31730   * @constructor
31731  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31732  * The container MUST have some type of size defined for the grid to fill. The container will be
31733  * automatically set to position relative if it isn't already.
31734  * @param {Object} config A config object that sets properties on this grid.
31735  */
31736 Roo.grid.Grid = function(container, config){
31737         // initialize the container
31738         this.container = Roo.get(container);
31739         this.container.update("");
31740         this.container.setStyle("overflow", "hidden");
31741     this.container.addClass('x-grid-container');
31742
31743     this.id = this.container.id;
31744
31745     Roo.apply(this, config);
31746     // check and correct shorthanded configs
31747     if(this.ds){
31748         this.dataSource = this.ds;
31749         delete this.ds;
31750     }
31751     if(this.cm){
31752         this.colModel = this.cm;
31753         delete this.cm;
31754     }
31755     if(this.sm){
31756         this.selModel = this.sm;
31757         delete this.sm;
31758     }
31759
31760     if (this.selModel) {
31761         this.selModel = Roo.factory(this.selModel, Roo.grid);
31762         this.sm = this.selModel;
31763         this.sm.xmodule = this.xmodule || false;
31764     }
31765     if (typeof(this.colModel.config) == 'undefined') {
31766         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31767         this.cm = this.colModel;
31768         this.cm.xmodule = this.xmodule || false;
31769     }
31770     if (this.dataSource) {
31771         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31772         this.ds = this.dataSource;
31773         this.ds.xmodule = this.xmodule || false;
31774          
31775     }
31776     
31777     
31778     
31779     if(this.width){
31780         this.container.setWidth(this.width);
31781     }
31782
31783     if(this.height){
31784         this.container.setHeight(this.height);
31785     }
31786     /** @private */
31787         this.addEvents({
31788         // raw events
31789         /**
31790          * @event click
31791          * The raw click event for the entire grid.
31792          * @param {Roo.EventObject} e
31793          */
31794         "click" : true,
31795         /**
31796          * @event dblclick
31797          * The raw dblclick event for the entire grid.
31798          * @param {Roo.EventObject} e
31799          */
31800         "dblclick" : true,
31801         /**
31802          * @event contextmenu
31803          * The raw contextmenu event for the entire grid.
31804          * @param {Roo.EventObject} e
31805          */
31806         "contextmenu" : true,
31807         /**
31808          * @event mousedown
31809          * The raw mousedown event for the entire grid.
31810          * @param {Roo.EventObject} e
31811          */
31812         "mousedown" : true,
31813         /**
31814          * @event mouseup
31815          * The raw mouseup event for the entire grid.
31816          * @param {Roo.EventObject} e
31817          */
31818         "mouseup" : true,
31819         /**
31820          * @event mouseover
31821          * The raw mouseover event for the entire grid.
31822          * @param {Roo.EventObject} e
31823          */
31824         "mouseover" : true,
31825         /**
31826          * @event mouseout
31827          * The raw mouseout event for the entire grid.
31828          * @param {Roo.EventObject} e
31829          */
31830         "mouseout" : true,
31831         /**
31832          * @event keypress
31833          * The raw keypress event for the entire grid.
31834          * @param {Roo.EventObject} e
31835          */
31836         "keypress" : true,
31837         /**
31838          * @event keydown
31839          * The raw keydown event for the entire grid.
31840          * @param {Roo.EventObject} e
31841          */
31842         "keydown" : true,
31843
31844         // custom events
31845
31846         /**
31847          * @event cellclick
31848          * Fires when a cell is clicked
31849          * @param {Grid} this
31850          * @param {Number} rowIndex
31851          * @param {Number} columnIndex
31852          * @param {Roo.EventObject} e
31853          */
31854         "cellclick" : true,
31855         /**
31856          * @event celldblclick
31857          * Fires when a cell is double clicked
31858          * @param {Grid} this
31859          * @param {Number} rowIndex
31860          * @param {Number} columnIndex
31861          * @param {Roo.EventObject} e
31862          */
31863         "celldblclick" : true,
31864         /**
31865          * @event rowclick
31866          * Fires when a row is clicked
31867          * @param {Grid} this
31868          * @param {Number} rowIndex
31869          * @param {Roo.EventObject} e
31870          */
31871         "rowclick" : true,
31872         /**
31873          * @event rowdblclick
31874          * Fires when a row is double clicked
31875          * @param {Grid} this
31876          * @param {Number} rowIndex
31877          * @param {Roo.EventObject} e
31878          */
31879         "rowdblclick" : true,
31880         /**
31881          * @event headerclick
31882          * Fires when a header is clicked
31883          * @param {Grid} this
31884          * @param {Number} columnIndex
31885          * @param {Roo.EventObject} e
31886          */
31887         "headerclick" : true,
31888         /**
31889          * @event headerdblclick
31890          * Fires when a header cell is double clicked
31891          * @param {Grid} this
31892          * @param {Number} columnIndex
31893          * @param {Roo.EventObject} e
31894          */
31895         "headerdblclick" : true,
31896         /**
31897          * @event rowcontextmenu
31898          * Fires when a row is right clicked
31899          * @param {Grid} this
31900          * @param {Number} rowIndex
31901          * @param {Roo.EventObject} e
31902          */
31903         "rowcontextmenu" : true,
31904         /**
31905          * @event cellcontextmenu
31906          * Fires when a cell is right clicked
31907          * @param {Grid} this
31908          * @param {Number} rowIndex
31909          * @param {Number} cellIndex
31910          * @param {Roo.EventObject} e
31911          */
31912          "cellcontextmenu" : true,
31913         /**
31914          * @event headercontextmenu
31915          * Fires when a header is right clicked
31916          * @param {Grid} this
31917          * @param {Number} columnIndex
31918          * @param {Roo.EventObject} e
31919          */
31920         "headercontextmenu" : true,
31921         /**
31922          * @event bodyscroll
31923          * Fires when the body element is scrolled
31924          * @param {Number} scrollLeft
31925          * @param {Number} scrollTop
31926          */
31927         "bodyscroll" : true,
31928         /**
31929          * @event columnresize
31930          * Fires when the user resizes a column
31931          * @param {Number} columnIndex
31932          * @param {Number} newSize
31933          */
31934         "columnresize" : true,
31935         /**
31936          * @event columnmove
31937          * Fires when the user moves a column
31938          * @param {Number} oldIndex
31939          * @param {Number} newIndex
31940          */
31941         "columnmove" : true,
31942         /**
31943          * @event startdrag
31944          * Fires when row(s) start being dragged
31945          * @param {Grid} this
31946          * @param {Roo.GridDD} dd The drag drop object
31947          * @param {event} e The raw browser event
31948          */
31949         "startdrag" : true,
31950         /**
31951          * @event enddrag
31952          * Fires when a drag operation is complete
31953          * @param {Grid} this
31954          * @param {Roo.GridDD} dd The drag drop object
31955          * @param {event} e The raw browser event
31956          */
31957         "enddrag" : true,
31958         /**
31959          * @event dragdrop
31960          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
31967         /**
31968          * @event dragover
31969          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31970          * @param {Grid} this
31971          * @param {Roo.GridDD} dd The drag drop object
31972          * @param {String} targetId The target drag drop object
31973          * @param {event} e The raw browser event
31974          */
31975         "dragover" : true,
31976         /**
31977          * @event dragenter
31978          *  Fires when the dragged row(s) first cross another DD target while being dragged
31979          * @param {Grid} this
31980          * @param {Roo.GridDD} dd The drag drop object
31981          * @param {String} targetId The target drag drop object
31982          * @param {event} e The raw browser event
31983          */
31984         "dragenter" : true,
31985         /**
31986          * @event dragout
31987          * Fires when the dragged row(s) leave another DD target while being dragged
31988          * @param {Grid} this
31989          * @param {Roo.GridDD} dd The drag drop object
31990          * @param {String} targetId The target drag drop object
31991          * @param {event} e The raw browser event
31992          */
31993         "dragout" : true,
31994         /**
31995          * @event rowclass
31996          * Fires when a row is rendered, so you can change add a style to it.
31997          * @param {GridView} gridview   The grid view
31998          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31999          */
32000         'rowclass' : true,
32001
32002         /**
32003          * @event render
32004          * Fires when the grid is rendered
32005          * @param {Grid} grid
32006          */
32007         'render' : true
32008     });
32009
32010     Roo.grid.Grid.superclass.constructor.call(this);
32011 };
32012 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32013     
32014     /**
32015      * @cfg {String} ddGroup - drag drop group.
32016      */
32017
32018     /**
32019      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32020      */
32021     minColumnWidth : 25,
32022
32023     /**
32024      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32025      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32026      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32027      */
32028     autoSizeColumns : false,
32029
32030     /**
32031      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32032      */
32033     autoSizeHeaders : true,
32034
32035     /**
32036      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32037      */
32038     monitorWindowResize : true,
32039
32040     /**
32041      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32042      * rows measured to get a columns size. Default is 0 (all rows).
32043      */
32044     maxRowsToMeasure : 0,
32045
32046     /**
32047      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32048      */
32049     trackMouseOver : true,
32050
32051     /**
32052     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32053     */
32054     
32055     /**
32056     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32057     */
32058     enableDragDrop : false,
32059     
32060     /**
32061     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32062     */
32063     enableColumnMove : true,
32064     
32065     /**
32066     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32067     */
32068     enableColumnHide : true,
32069     
32070     /**
32071     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32072     */
32073     enableRowHeightSync : false,
32074     
32075     /**
32076     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32077     */
32078     stripeRows : true,
32079     
32080     /**
32081     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32082     */
32083     autoHeight : false,
32084
32085     /**
32086      * @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.
32087      */
32088     autoExpandColumn : false,
32089
32090     /**
32091     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32092     * Default is 50.
32093     */
32094     autoExpandMin : 50,
32095
32096     /**
32097     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32098     */
32099     autoExpandMax : 1000,
32100
32101     /**
32102     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32103     */
32104     view : null,
32105
32106     /**
32107     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32108     */
32109     loadMask : false,
32110     /**
32111     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32112     */
32113     dropTarget: false,
32114     
32115    
32116     
32117     // private
32118     rendered : false,
32119
32120     /**
32121     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32122     * of a fixed width. Default is false.
32123     */
32124     /**
32125     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32126     */
32127     /**
32128      * Called once after all setup has been completed and the grid is ready to be rendered.
32129      * @return {Roo.grid.Grid} this
32130      */
32131     render : function()
32132     {
32133         var c = this.container;
32134         // try to detect autoHeight/width mode
32135         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32136             this.autoHeight = true;
32137         }
32138         var view = this.getView();
32139         view.init(this);
32140
32141         c.on("click", this.onClick, this);
32142         c.on("dblclick", this.onDblClick, this);
32143         c.on("contextmenu", this.onContextMenu, this);
32144         c.on("keydown", this.onKeyDown, this);
32145         if (Roo.isTouch) {
32146             c.on("touchstart", this.onTouchStart, this);
32147         }
32148
32149         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32150
32151         this.getSelectionModel().init(this);
32152
32153         view.render();
32154
32155         if(this.loadMask){
32156             this.loadMask = new Roo.LoadMask(this.container,
32157                     Roo.apply({store:this.dataSource}, this.loadMask));
32158         }
32159         
32160         
32161         if (this.toolbar && this.toolbar.xtype) {
32162             this.toolbar.container = this.getView().getHeaderPanel(true);
32163             this.toolbar = new Roo.Toolbar(this.toolbar);
32164         }
32165         if (this.footer && this.footer.xtype) {
32166             this.footer.dataSource = this.getDataSource();
32167             this.footer.container = this.getView().getFooterPanel(true);
32168             this.footer = Roo.factory(this.footer, Roo);
32169         }
32170         if (this.dropTarget && this.dropTarget.xtype) {
32171             delete this.dropTarget.xtype;
32172             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32173         }
32174         
32175         
32176         this.rendered = true;
32177         this.fireEvent('render', this);
32178         return this;
32179     },
32180
32181         /**
32182          * Reconfigures the grid to use a different Store and Column Model.
32183          * The View will be bound to the new objects and refreshed.
32184          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32185          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32186          */
32187     reconfigure : function(dataSource, colModel){
32188         if(this.loadMask){
32189             this.loadMask.destroy();
32190             this.loadMask = new Roo.LoadMask(this.container,
32191                     Roo.apply({store:dataSource}, this.loadMask));
32192         }
32193         this.view.bind(dataSource, colModel);
32194         this.dataSource = dataSource;
32195         this.colModel = colModel;
32196         this.view.refresh(true);
32197     },
32198
32199     // private
32200     onKeyDown : function(e){
32201         this.fireEvent("keydown", e);
32202     },
32203
32204     /**
32205      * Destroy this grid.
32206      * @param {Boolean} removeEl True to remove the element
32207      */
32208     destroy : function(removeEl, keepListeners){
32209         if(this.loadMask){
32210             this.loadMask.destroy();
32211         }
32212         var c = this.container;
32213         c.removeAllListeners();
32214         this.view.destroy();
32215         this.colModel.purgeListeners();
32216         if(!keepListeners){
32217             this.purgeListeners();
32218         }
32219         c.update("");
32220         if(removeEl === true){
32221             c.remove();
32222         }
32223     },
32224
32225     // private
32226     processEvent : function(name, e){
32227         // does this fire select???
32228         //Roo.log('grid:processEvent '  + name);
32229         
32230         if (name != 'touchstart' ) {
32231             this.fireEvent(name, e);    
32232         }
32233         
32234         var t = e.getTarget();
32235         var v = this.view;
32236         var header = v.findHeaderIndex(t);
32237         if(header !== false){
32238             var ename = name == 'touchstart' ? 'click' : name;
32239              
32240             this.fireEvent("header" + ename, this, header, e);
32241         }else{
32242             var row = v.findRowIndex(t);
32243             var cell = v.findCellIndex(t);
32244             if (name == 'touchstart') {
32245                 // first touch is always a click.
32246                 // hopefull this happens after selection is updated.?
32247                 name = false;
32248                 
32249                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32250                     var cs = this.selModel.getSelectedCell();
32251                     if (row == cs[0] && cell == cs[1]){
32252                         name = 'dblclick';
32253                     }
32254                 }
32255                 if (typeof(this.selModel.getSelections) != 'undefined') {
32256                     var cs = this.selModel.getSelections();
32257                     var ds = this.dataSource;
32258                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32259                         name = 'dblclick';
32260                     }
32261                 }
32262                 if (!name) {
32263                     return;
32264                 }
32265             }
32266             
32267             
32268             if(row !== false){
32269                 this.fireEvent("row" + name, this, row, e);
32270                 if(cell !== false){
32271                     this.fireEvent("cell" + name, this, row, cell, e);
32272                 }
32273             }
32274         }
32275     },
32276
32277     // private
32278     onClick : function(e){
32279         this.processEvent("click", e);
32280     },
32281    // private
32282     onTouchStart : function(e){
32283         this.processEvent("touchstart", e);
32284     },
32285
32286     // private
32287     onContextMenu : function(e, t){
32288         this.processEvent("contextmenu", e);
32289     },
32290
32291     // private
32292     onDblClick : function(e){
32293         this.processEvent("dblclick", e);
32294     },
32295
32296     // private
32297     walkCells : function(row, col, step, fn, scope){
32298         var cm = this.colModel, clen = cm.getColumnCount();
32299         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32300         if(step < 0){
32301             if(col < 0){
32302                 row--;
32303                 first = false;
32304             }
32305             while(row >= 0){
32306                 if(!first){
32307                     col = clen-1;
32308                 }
32309                 first = false;
32310                 while(col >= 0){
32311                     if(fn.call(scope || this, row, col, cm) === true){
32312                         return [row, col];
32313                     }
32314                     col--;
32315                 }
32316                 row--;
32317             }
32318         } else {
32319             if(col >= clen){
32320                 row++;
32321                 first = false;
32322             }
32323             while(row < rlen){
32324                 if(!first){
32325                     col = 0;
32326                 }
32327                 first = false;
32328                 while(col < clen){
32329                     if(fn.call(scope || this, row, col, cm) === true){
32330                         return [row, col];
32331                     }
32332                     col++;
32333                 }
32334                 row++;
32335             }
32336         }
32337         return null;
32338     },
32339
32340     // private
32341     getSelections : function(){
32342         return this.selModel.getSelections();
32343     },
32344
32345     /**
32346      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32347      * but if manual update is required this method will initiate it.
32348      */
32349     autoSize : function(){
32350         if(this.rendered){
32351             this.view.layout();
32352             if(this.view.adjustForScroll){
32353                 this.view.adjustForScroll();
32354             }
32355         }
32356     },
32357
32358     /**
32359      * Returns the grid's underlying element.
32360      * @return {Element} The element
32361      */
32362     getGridEl : function(){
32363         return this.container;
32364     },
32365
32366     // private for compatibility, overridden by editor grid
32367     stopEditing : function(){},
32368
32369     /**
32370      * Returns the grid's SelectionModel.
32371      * @return {SelectionModel}
32372      */
32373     getSelectionModel : function(){
32374         if(!this.selModel){
32375             this.selModel = new Roo.grid.RowSelectionModel();
32376         }
32377         return this.selModel;
32378     },
32379
32380     /**
32381      * Returns the grid's DataSource.
32382      * @return {DataSource}
32383      */
32384     getDataSource : function(){
32385         return this.dataSource;
32386     },
32387
32388     /**
32389      * Returns the grid's ColumnModel.
32390      * @return {ColumnModel}
32391      */
32392     getColumnModel : function(){
32393         return this.colModel;
32394     },
32395
32396     /**
32397      * Returns the grid's GridView object.
32398      * @return {GridView}
32399      */
32400     getView : function(){
32401         if(!this.view){
32402             this.view = new Roo.grid.GridView(this.viewConfig);
32403         }
32404         return this.view;
32405     },
32406     /**
32407      * Called to get grid's drag proxy text, by default returns this.ddText.
32408      * @return {String}
32409      */
32410     getDragDropText : function(){
32411         var count = this.selModel.getCount();
32412         return String.format(this.ddText, count, count == 1 ? '' : 's');
32413     }
32414 });
32415 /**
32416  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32417  * %0 is replaced with the number of selected rows.
32418  * @type String
32419  */
32420 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32421  * Based on:
32422  * Ext JS Library 1.1.1
32423  * Copyright(c) 2006-2007, Ext JS, LLC.
32424  *
32425  * Originally Released Under LGPL - original licence link has changed is not relivant.
32426  *
32427  * Fork - LGPL
32428  * <script type="text/javascript">
32429  */
32430  
32431 Roo.grid.AbstractGridView = function(){
32432         this.grid = null;
32433         
32434         this.events = {
32435             "beforerowremoved" : true,
32436             "beforerowsinserted" : true,
32437             "beforerefresh" : true,
32438             "rowremoved" : true,
32439             "rowsinserted" : true,
32440             "rowupdated" : true,
32441             "refresh" : true
32442         };
32443     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32444 };
32445
32446 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32447     rowClass : "x-grid-row",
32448     cellClass : "x-grid-cell",
32449     tdClass : "x-grid-td",
32450     hdClass : "x-grid-hd",
32451     splitClass : "x-grid-hd-split",
32452     
32453     init: function(grid){
32454         this.grid = grid;
32455                 var cid = this.grid.getGridEl().id;
32456         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32457         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32458         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32459         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32460         },
32461         
32462     getColumnRenderers : function(){
32463         var renderers = [];
32464         var cm = this.grid.colModel;
32465         var colCount = cm.getColumnCount();
32466         for(var i = 0; i < colCount; i++){
32467             renderers[i] = cm.getRenderer(i);
32468         }
32469         return renderers;
32470     },
32471     
32472     getColumnIds : function(){
32473         var ids = [];
32474         var cm = this.grid.colModel;
32475         var colCount = cm.getColumnCount();
32476         for(var i = 0; i < colCount; i++){
32477             ids[i] = cm.getColumnId(i);
32478         }
32479         return ids;
32480     },
32481     
32482     getDataIndexes : function(){
32483         if(!this.indexMap){
32484             this.indexMap = this.buildIndexMap();
32485         }
32486         return this.indexMap.colToData;
32487     },
32488     
32489     getColumnIndexByDataIndex : function(dataIndex){
32490         if(!this.indexMap){
32491             this.indexMap = this.buildIndexMap();
32492         }
32493         return this.indexMap.dataToCol[dataIndex];
32494     },
32495     
32496     /**
32497      * Set a css style for a column dynamically. 
32498      * @param {Number} colIndex The index of the column
32499      * @param {String} name The css property name
32500      * @param {String} value The css value
32501      */
32502     setCSSStyle : function(colIndex, name, value){
32503         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32504         Roo.util.CSS.updateRule(selector, name, value);
32505     },
32506     
32507     generateRules : function(cm){
32508         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32509         Roo.util.CSS.removeStyleSheet(rulesId);
32510         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32511             var cid = cm.getColumnId(i);
32512             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32513                          this.tdSelector, cid, " {\n}\n",
32514                          this.hdSelector, cid, " {\n}\n",
32515                          this.splitSelector, cid, " {\n}\n");
32516         }
32517         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32518     }
32519 });/*
32520  * Based on:
32521  * Ext JS Library 1.1.1
32522  * Copyright(c) 2006-2007, Ext JS, LLC.
32523  *
32524  * Originally Released Under LGPL - original licence link has changed is not relivant.
32525  *
32526  * Fork - LGPL
32527  * <script type="text/javascript">
32528  */
32529
32530 // private
32531 // This is a support class used internally by the Grid components
32532 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32533     this.grid = grid;
32534     this.view = grid.getView();
32535     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32536     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32537     if(hd2){
32538         this.setHandleElId(Roo.id(hd));
32539         this.setOuterHandleElId(Roo.id(hd2));
32540     }
32541     this.scroll = false;
32542 };
32543 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32544     maxDragWidth: 120,
32545     getDragData : function(e){
32546         var t = Roo.lib.Event.getTarget(e);
32547         var h = this.view.findHeaderCell(t);
32548         if(h){
32549             return {ddel: h.firstChild, header:h};
32550         }
32551         return false;
32552     },
32553
32554     onInitDrag : function(e){
32555         this.view.headersDisabled = true;
32556         var clone = this.dragData.ddel.cloneNode(true);
32557         clone.id = Roo.id();
32558         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32559         this.proxy.update(clone);
32560         return true;
32561     },
32562
32563     afterValidDrop : function(){
32564         var v = this.view;
32565         setTimeout(function(){
32566             v.headersDisabled = false;
32567         }, 50);
32568     },
32569
32570     afterInvalidDrop : function(){
32571         var v = this.view;
32572         setTimeout(function(){
32573             v.headersDisabled = false;
32574         }, 50);
32575     }
32576 });
32577 /*
32578  * Based on:
32579  * Ext JS Library 1.1.1
32580  * Copyright(c) 2006-2007, Ext JS, LLC.
32581  *
32582  * Originally Released Under LGPL - original licence link has changed is not relivant.
32583  *
32584  * Fork - LGPL
32585  * <script type="text/javascript">
32586  */
32587 // private
32588 // This is a support class used internally by the Grid components
32589 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32590     this.grid = grid;
32591     this.view = grid.getView();
32592     // split the proxies so they don't interfere with mouse events
32593     this.proxyTop = Roo.DomHelper.append(document.body, {
32594         cls:"col-move-top", html:"&#160;"
32595     }, true);
32596     this.proxyBottom = Roo.DomHelper.append(document.body, {
32597         cls:"col-move-bottom", html:"&#160;"
32598     }, true);
32599     this.proxyTop.hide = this.proxyBottom.hide = function(){
32600         this.setLeftTop(-100,-100);
32601         this.setStyle("visibility", "hidden");
32602     };
32603     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32604     // temporarily disabled
32605     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32606     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32607 };
32608 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32609     proxyOffsets : [-4, -9],
32610     fly: Roo.Element.fly,
32611
32612     getTargetFromEvent : function(e){
32613         var t = Roo.lib.Event.getTarget(e);
32614         var cindex = this.view.findCellIndex(t);
32615         if(cindex !== false){
32616             return this.view.getHeaderCell(cindex);
32617         }
32618         return null;
32619     },
32620
32621     nextVisible : function(h){
32622         var v = this.view, cm = this.grid.colModel;
32623         h = h.nextSibling;
32624         while(h){
32625             if(!cm.isHidden(v.getCellIndex(h))){
32626                 return h;
32627             }
32628             h = h.nextSibling;
32629         }
32630         return null;
32631     },
32632
32633     prevVisible : function(h){
32634         var v = this.view, cm = this.grid.colModel;
32635         h = h.prevSibling;
32636         while(h){
32637             if(!cm.isHidden(v.getCellIndex(h))){
32638                 return h;
32639             }
32640             h = h.prevSibling;
32641         }
32642         return null;
32643     },
32644
32645     positionIndicator : function(h, n, e){
32646         var x = Roo.lib.Event.getPageX(e);
32647         var r = Roo.lib.Dom.getRegion(n.firstChild);
32648         var px, pt, py = r.top + this.proxyOffsets[1];
32649         if((r.right - x) <= (r.right-r.left)/2){
32650             px = r.right+this.view.borderWidth;
32651             pt = "after";
32652         }else{
32653             px = r.left;
32654             pt = "before";
32655         }
32656         var oldIndex = this.view.getCellIndex(h);
32657         var newIndex = this.view.getCellIndex(n);
32658
32659         if(this.grid.colModel.isFixed(newIndex)){
32660             return false;
32661         }
32662
32663         var locked = this.grid.colModel.isLocked(newIndex);
32664
32665         if(pt == "after"){
32666             newIndex++;
32667         }
32668         if(oldIndex < newIndex){
32669             newIndex--;
32670         }
32671         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32672             return false;
32673         }
32674         px +=  this.proxyOffsets[0];
32675         this.proxyTop.setLeftTop(px, py);
32676         this.proxyTop.show();
32677         if(!this.bottomOffset){
32678             this.bottomOffset = this.view.mainHd.getHeight();
32679         }
32680         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32681         this.proxyBottom.show();
32682         return pt;
32683     },
32684
32685     onNodeEnter : function(n, dd, e, data){
32686         if(data.header != n){
32687             this.positionIndicator(data.header, n, e);
32688         }
32689     },
32690
32691     onNodeOver : function(n, dd, e, data){
32692         var result = false;
32693         if(data.header != n){
32694             result = this.positionIndicator(data.header, n, e);
32695         }
32696         if(!result){
32697             this.proxyTop.hide();
32698             this.proxyBottom.hide();
32699         }
32700         return result ? this.dropAllowed : this.dropNotAllowed;
32701     },
32702
32703     onNodeOut : function(n, dd, e, data){
32704         this.proxyTop.hide();
32705         this.proxyBottom.hide();
32706     },
32707
32708     onNodeDrop : function(n, dd, e, data){
32709         var h = data.header;
32710         if(h != n){
32711             var cm = this.grid.colModel;
32712             var x = Roo.lib.Event.getPageX(e);
32713             var r = Roo.lib.Dom.getRegion(n.firstChild);
32714             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32715             var oldIndex = this.view.getCellIndex(h);
32716             var newIndex = this.view.getCellIndex(n);
32717             var locked = cm.isLocked(newIndex);
32718             if(pt == "after"){
32719                 newIndex++;
32720             }
32721             if(oldIndex < newIndex){
32722                 newIndex--;
32723             }
32724             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32725                 return false;
32726             }
32727             cm.setLocked(oldIndex, locked, true);
32728             cm.moveColumn(oldIndex, newIndex);
32729             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32730             return true;
32731         }
32732         return false;
32733     }
32734 });
32735 /*
32736  * Based on:
32737  * Ext JS Library 1.1.1
32738  * Copyright(c) 2006-2007, Ext JS, LLC.
32739  *
32740  * Originally Released Under LGPL - original licence link has changed is not relivant.
32741  *
32742  * Fork - LGPL
32743  * <script type="text/javascript">
32744  */
32745   
32746 /**
32747  * @class Roo.grid.GridView
32748  * @extends Roo.util.Observable
32749  *
32750  * @constructor
32751  * @param {Object} config
32752  */
32753 Roo.grid.GridView = function(config){
32754     Roo.grid.GridView.superclass.constructor.call(this);
32755     this.el = null;
32756
32757     Roo.apply(this, config);
32758 };
32759
32760 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32761
32762     unselectable :  'unselectable="on"',
32763     unselectableCls :  'x-unselectable',
32764     
32765     
32766     rowClass : "x-grid-row",
32767
32768     cellClass : "x-grid-col",
32769
32770     tdClass : "x-grid-td",
32771
32772     hdClass : "x-grid-hd",
32773
32774     splitClass : "x-grid-split",
32775
32776     sortClasses : ["sort-asc", "sort-desc"],
32777
32778     enableMoveAnim : false,
32779
32780     hlColor: "C3DAF9",
32781
32782     dh : Roo.DomHelper,
32783
32784     fly : Roo.Element.fly,
32785
32786     css : Roo.util.CSS,
32787
32788     borderWidth: 1,
32789
32790     splitOffset: 3,
32791
32792     scrollIncrement : 22,
32793
32794     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32795
32796     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32797
32798     bind : function(ds, cm){
32799         if(this.ds){
32800             this.ds.un("load", this.onLoad, this);
32801             this.ds.un("datachanged", this.onDataChange, this);
32802             this.ds.un("add", this.onAdd, this);
32803             this.ds.un("remove", this.onRemove, this);
32804             this.ds.un("update", this.onUpdate, this);
32805             this.ds.un("clear", this.onClear, this);
32806         }
32807         if(ds){
32808             ds.on("load", this.onLoad, this);
32809             ds.on("datachanged", this.onDataChange, this);
32810             ds.on("add", this.onAdd, this);
32811             ds.on("remove", this.onRemove, this);
32812             ds.on("update", this.onUpdate, this);
32813             ds.on("clear", this.onClear, this);
32814         }
32815         this.ds = ds;
32816
32817         if(this.cm){
32818             this.cm.un("widthchange", this.onColWidthChange, this);
32819             this.cm.un("headerchange", this.onHeaderChange, this);
32820             this.cm.un("hiddenchange", this.onHiddenChange, this);
32821             this.cm.un("columnmoved", this.onColumnMove, this);
32822             this.cm.un("columnlockchange", this.onColumnLock, this);
32823         }
32824         if(cm){
32825             this.generateRules(cm);
32826             cm.on("widthchange", this.onColWidthChange, this);
32827             cm.on("headerchange", this.onHeaderChange, this);
32828             cm.on("hiddenchange", this.onHiddenChange, this);
32829             cm.on("columnmoved", this.onColumnMove, this);
32830             cm.on("columnlockchange", this.onColumnLock, this);
32831         }
32832         this.cm = cm;
32833     },
32834
32835     init: function(grid){
32836         Roo.grid.GridView.superclass.init.call(this, grid);
32837
32838         this.bind(grid.dataSource, grid.colModel);
32839
32840         grid.on("headerclick", this.handleHeaderClick, this);
32841
32842         if(grid.trackMouseOver){
32843             grid.on("mouseover", this.onRowOver, this);
32844             grid.on("mouseout", this.onRowOut, this);
32845         }
32846         grid.cancelTextSelection = function(){};
32847         this.gridId = grid.id;
32848
32849         var tpls = this.templates || {};
32850
32851         if(!tpls.master){
32852             tpls.master = new Roo.Template(
32853                '<div class="x-grid" hidefocus="true">',
32854                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32855                   '<div class="x-grid-topbar"></div>',
32856                   '<div class="x-grid-scroller"><div></div></div>',
32857                   '<div class="x-grid-locked">',
32858                       '<div class="x-grid-header">{lockedHeader}</div>',
32859                       '<div class="x-grid-body">{lockedBody}</div>',
32860                   "</div>",
32861                   '<div class="x-grid-viewport">',
32862                       '<div class="x-grid-header">{header}</div>',
32863                       '<div class="x-grid-body">{body}</div>',
32864                   "</div>",
32865                   '<div class="x-grid-bottombar"></div>',
32866                  
32867                   '<div class="x-grid-resize-proxy">&#160;</div>',
32868                "</div>"
32869             );
32870             tpls.master.disableformats = true;
32871         }
32872
32873         if(!tpls.header){
32874             tpls.header = new Roo.Template(
32875                '<table border="0" cellspacing="0" cellpadding="0">',
32876                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32877                "</table>{splits}"
32878             );
32879             tpls.header.disableformats = true;
32880         }
32881         tpls.header.compile();
32882
32883         if(!tpls.hcell){
32884             tpls.hcell = new Roo.Template(
32885                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32886                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32887                 "</div></td>"
32888              );
32889              tpls.hcell.disableFormats = true;
32890         }
32891         tpls.hcell.compile();
32892
32893         if(!tpls.hsplit){
32894             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32895                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32896             tpls.hsplit.disableFormats = true;
32897         }
32898         tpls.hsplit.compile();
32899
32900         if(!tpls.body){
32901             tpls.body = new Roo.Template(
32902                '<table border="0" cellspacing="0" cellpadding="0">',
32903                "<tbody>{rows}</tbody>",
32904                "</table>"
32905             );
32906             tpls.body.disableFormats = true;
32907         }
32908         tpls.body.compile();
32909
32910         if(!tpls.row){
32911             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32912             tpls.row.disableFormats = true;
32913         }
32914         tpls.row.compile();
32915
32916         if(!tpls.cell){
32917             tpls.cell = new Roo.Template(
32918                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32919                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32920                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32921                 "</td>"
32922             );
32923             tpls.cell.disableFormats = true;
32924         }
32925         tpls.cell.compile();
32926
32927         this.templates = tpls;
32928     },
32929
32930     // remap these for backwards compat
32931     onColWidthChange : function(){
32932         this.updateColumns.apply(this, arguments);
32933     },
32934     onHeaderChange : function(){
32935         this.updateHeaders.apply(this, arguments);
32936     }, 
32937     onHiddenChange : function(){
32938         this.handleHiddenChange.apply(this, arguments);
32939     },
32940     onColumnMove : function(){
32941         this.handleColumnMove.apply(this, arguments);
32942     },
32943     onColumnLock : function(){
32944         this.handleLockChange.apply(this, arguments);
32945     },
32946
32947     onDataChange : function(){
32948         this.refresh();
32949         this.updateHeaderSortState();
32950     },
32951
32952     onClear : function(){
32953         this.refresh();
32954     },
32955
32956     onUpdate : function(ds, record){
32957         this.refreshRow(record);
32958     },
32959
32960     refreshRow : function(record){
32961         var ds = this.ds, index;
32962         if(typeof record == 'number'){
32963             index = record;
32964             record = ds.getAt(index);
32965         }else{
32966             index = ds.indexOf(record);
32967         }
32968         this.insertRows(ds, index, index, true);
32969         this.onRemove(ds, record, index+1, true);
32970         this.syncRowHeights(index, index);
32971         this.layout();
32972         this.fireEvent("rowupdated", this, index, record);
32973     },
32974
32975     onAdd : function(ds, records, index){
32976         this.insertRows(ds, index, index + (records.length-1));
32977     },
32978
32979     onRemove : function(ds, record, index, isUpdate){
32980         if(isUpdate !== true){
32981             this.fireEvent("beforerowremoved", this, index, record);
32982         }
32983         var bt = this.getBodyTable(), lt = this.getLockedTable();
32984         if(bt.rows[index]){
32985             bt.firstChild.removeChild(bt.rows[index]);
32986         }
32987         if(lt.rows[index]){
32988             lt.firstChild.removeChild(lt.rows[index]);
32989         }
32990         if(isUpdate !== true){
32991             this.stripeRows(index);
32992             this.syncRowHeights(index, index);
32993             this.layout();
32994             this.fireEvent("rowremoved", this, index, record);
32995         }
32996     },
32997
32998     onLoad : function(){
32999         this.scrollToTop();
33000     },
33001
33002     /**
33003      * Scrolls the grid to the top
33004      */
33005     scrollToTop : function(){
33006         if(this.scroller){
33007             this.scroller.dom.scrollTop = 0;
33008             this.syncScroll();
33009         }
33010     },
33011
33012     /**
33013      * Gets a panel in the header of the grid that can be used for toolbars etc.
33014      * After modifying the contents of this panel a call to grid.autoSize() may be
33015      * required to register any changes in size.
33016      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33017      * @return Roo.Element
33018      */
33019     getHeaderPanel : function(doShow){
33020         if(doShow){
33021             this.headerPanel.show();
33022         }
33023         return this.headerPanel;
33024     },
33025
33026     /**
33027      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33028      * After modifying the contents of this panel a call to grid.autoSize() may be
33029      * required to register any changes in size.
33030      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33031      * @return Roo.Element
33032      */
33033     getFooterPanel : function(doShow){
33034         if(doShow){
33035             this.footerPanel.show();
33036         }
33037         return this.footerPanel;
33038     },
33039
33040     initElements : function(){
33041         var E = Roo.Element;
33042         var el = this.grid.getGridEl().dom.firstChild;
33043         var cs = el.childNodes;
33044
33045         this.el = new E(el);
33046         
33047          this.focusEl = new E(el.firstChild);
33048         this.focusEl.swallowEvent("click", true);
33049         
33050         this.headerPanel = new E(cs[1]);
33051         this.headerPanel.enableDisplayMode("block");
33052
33053         this.scroller = new E(cs[2]);
33054         this.scrollSizer = new E(this.scroller.dom.firstChild);
33055
33056         this.lockedWrap = new E(cs[3]);
33057         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33058         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33059
33060         this.mainWrap = new E(cs[4]);
33061         this.mainHd = new E(this.mainWrap.dom.firstChild);
33062         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33063
33064         this.footerPanel = new E(cs[5]);
33065         this.footerPanel.enableDisplayMode("block");
33066
33067         this.resizeProxy = new E(cs[6]);
33068
33069         this.headerSelector = String.format(
33070            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33071            this.lockedHd.id, this.mainHd.id
33072         );
33073
33074         this.splitterSelector = String.format(
33075            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33076            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33077         );
33078     },
33079     idToCssName : function(s)
33080     {
33081         return s.replace(/[^a-z0-9]+/ig, '-');
33082     },
33083
33084     getHeaderCell : function(index){
33085         return Roo.DomQuery.select(this.headerSelector)[index];
33086     },
33087
33088     getHeaderCellMeasure : function(index){
33089         return this.getHeaderCell(index).firstChild;
33090     },
33091
33092     getHeaderCellText : function(index){
33093         return this.getHeaderCell(index).firstChild.firstChild;
33094     },
33095
33096     getLockedTable : function(){
33097         return this.lockedBody.dom.firstChild;
33098     },
33099
33100     getBodyTable : function(){
33101         return this.mainBody.dom.firstChild;
33102     },
33103
33104     getLockedRow : function(index){
33105         return this.getLockedTable().rows[index];
33106     },
33107
33108     getRow : function(index){
33109         return this.getBodyTable().rows[index];
33110     },
33111
33112     getRowComposite : function(index){
33113         if(!this.rowEl){
33114             this.rowEl = new Roo.CompositeElementLite();
33115         }
33116         var els = [], lrow, mrow;
33117         if(lrow = this.getLockedRow(index)){
33118             els.push(lrow);
33119         }
33120         if(mrow = this.getRow(index)){
33121             els.push(mrow);
33122         }
33123         this.rowEl.elements = els;
33124         return this.rowEl;
33125     },
33126     /**
33127      * Gets the 'td' of the cell
33128      * 
33129      * @param {Integer} rowIndex row to select
33130      * @param {Integer} colIndex column to select
33131      * 
33132      * @return {Object} 
33133      */
33134     getCell : function(rowIndex, colIndex){
33135         var locked = this.cm.getLockedCount();
33136         var source;
33137         if(colIndex < locked){
33138             source = this.lockedBody.dom.firstChild;
33139         }else{
33140             source = this.mainBody.dom.firstChild;
33141             colIndex -= locked;
33142         }
33143         return source.rows[rowIndex].childNodes[colIndex];
33144     },
33145
33146     getCellText : function(rowIndex, colIndex){
33147         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33148     },
33149
33150     getCellBox : function(cell){
33151         var b = this.fly(cell).getBox();
33152         if(Roo.isOpera){ // opera fails to report the Y
33153             b.y = cell.offsetTop + this.mainBody.getY();
33154         }
33155         return b;
33156     },
33157
33158     getCellIndex : function(cell){
33159         var id = String(cell.className).match(this.cellRE);
33160         if(id){
33161             return parseInt(id[1], 10);
33162         }
33163         return 0;
33164     },
33165
33166     findHeaderIndex : function(n){
33167         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33168         return r ? this.getCellIndex(r) : false;
33169     },
33170
33171     findHeaderCell : function(n){
33172         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33173         return r ? r : false;
33174     },
33175
33176     findRowIndex : function(n){
33177         if(!n){
33178             return false;
33179         }
33180         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33181         return r ? r.rowIndex : false;
33182     },
33183
33184     findCellIndex : function(node){
33185         var stop = this.el.dom;
33186         while(node && node != stop){
33187             if(this.findRE.test(node.className)){
33188                 return this.getCellIndex(node);
33189             }
33190             node = node.parentNode;
33191         }
33192         return false;
33193     },
33194
33195     getColumnId : function(index){
33196         return this.cm.getColumnId(index);
33197     },
33198
33199     getSplitters : function()
33200     {
33201         if(this.splitterSelector){
33202            return Roo.DomQuery.select(this.splitterSelector);
33203         }else{
33204             return null;
33205       }
33206     },
33207
33208     getSplitter : function(index){
33209         return this.getSplitters()[index];
33210     },
33211
33212     onRowOver : function(e, t){
33213         var row;
33214         if((row = this.findRowIndex(t)) !== false){
33215             this.getRowComposite(row).addClass("x-grid-row-over");
33216         }
33217     },
33218
33219     onRowOut : function(e, t){
33220         var row;
33221         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33222             this.getRowComposite(row).removeClass("x-grid-row-over");
33223         }
33224     },
33225
33226     renderHeaders : function(){
33227         var cm = this.cm;
33228         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33229         var cb = [], lb = [], sb = [], lsb = [], p = {};
33230         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33231             p.cellId = "x-grid-hd-0-" + i;
33232             p.splitId = "x-grid-csplit-0-" + i;
33233             p.id = cm.getColumnId(i);
33234             p.value = cm.getColumnHeader(i) || "";
33235             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33236             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33237             if(!cm.isLocked(i)){
33238                 cb[cb.length] = ct.apply(p);
33239                 sb[sb.length] = st.apply(p);
33240             }else{
33241                 lb[lb.length] = ct.apply(p);
33242                 lsb[lsb.length] = st.apply(p);
33243             }
33244         }
33245         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33246                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33247     },
33248
33249     updateHeaders : function(){
33250         var html = this.renderHeaders();
33251         this.lockedHd.update(html[0]);
33252         this.mainHd.update(html[1]);
33253     },
33254
33255     /**
33256      * Focuses the specified row.
33257      * @param {Number} row The row index
33258      */
33259     focusRow : function(row)
33260     {
33261         //Roo.log('GridView.focusRow');
33262         var x = this.scroller.dom.scrollLeft;
33263         this.focusCell(row, 0, false);
33264         this.scroller.dom.scrollLeft = x;
33265     },
33266
33267     /**
33268      * Focuses the specified cell.
33269      * @param {Number} row The row index
33270      * @param {Number} col The column index
33271      * @param {Boolean} hscroll false to disable horizontal scrolling
33272      */
33273     focusCell : function(row, col, hscroll)
33274     {
33275         //Roo.log('GridView.focusCell');
33276         var el = this.ensureVisible(row, col, hscroll);
33277         this.focusEl.alignTo(el, "tl-tl");
33278         if(Roo.isGecko){
33279             this.focusEl.focus();
33280         }else{
33281             this.focusEl.focus.defer(1, this.focusEl);
33282         }
33283     },
33284
33285     /**
33286      * Scrolls the specified cell into view
33287      * @param {Number} row The row index
33288      * @param {Number} col The column index
33289      * @param {Boolean} hscroll false to disable horizontal scrolling
33290      */
33291     ensureVisible : function(row, col, hscroll)
33292     {
33293         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33294         //return null; //disable for testing.
33295         if(typeof row != "number"){
33296             row = row.rowIndex;
33297         }
33298         if(row < 0 && row >= this.ds.getCount()){
33299             return  null;
33300         }
33301         col = (col !== undefined ? col : 0);
33302         var cm = this.grid.colModel;
33303         while(cm.isHidden(col)){
33304             col++;
33305         }
33306
33307         var el = this.getCell(row, col);
33308         if(!el){
33309             return null;
33310         }
33311         var c = this.scroller.dom;
33312
33313         var ctop = parseInt(el.offsetTop, 10);
33314         var cleft = parseInt(el.offsetLeft, 10);
33315         var cbot = ctop + el.offsetHeight;
33316         var cright = cleft + el.offsetWidth;
33317         
33318         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33319         var stop = parseInt(c.scrollTop, 10);
33320         var sleft = parseInt(c.scrollLeft, 10);
33321         var sbot = stop + ch;
33322         var sright = sleft + c.clientWidth;
33323         /*
33324         Roo.log('GridView.ensureVisible:' +
33325                 ' ctop:' + ctop +
33326                 ' c.clientHeight:' + c.clientHeight +
33327                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33328                 ' stop:' + stop +
33329                 ' cbot:' + cbot +
33330                 ' sbot:' + sbot +
33331                 ' ch:' + ch  
33332                 );
33333         */
33334         if(ctop < stop){
33335              c.scrollTop = ctop;
33336             //Roo.log("set scrolltop to ctop DISABLE?");
33337         }else if(cbot > sbot){
33338             //Roo.log("set scrolltop to cbot-ch");
33339             c.scrollTop = cbot-ch;
33340         }
33341         
33342         if(hscroll !== false){
33343             if(cleft < sleft){
33344                 c.scrollLeft = cleft;
33345             }else if(cright > sright){
33346                 c.scrollLeft = cright-c.clientWidth;
33347             }
33348         }
33349          
33350         return el;
33351     },
33352
33353     updateColumns : function(){
33354         this.grid.stopEditing();
33355         var cm = this.grid.colModel, colIds = this.getColumnIds();
33356         //var totalWidth = cm.getTotalWidth();
33357         var pos = 0;
33358         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33359             //if(cm.isHidden(i)) continue;
33360             var w = cm.getColumnWidth(i);
33361             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33362             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33363         }
33364         this.updateSplitters();
33365     },
33366
33367     generateRules : function(cm){
33368         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33369         Roo.util.CSS.removeStyleSheet(rulesId);
33370         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33371             var cid = cm.getColumnId(i);
33372             var align = '';
33373             if(cm.config[i].align){
33374                 align = 'text-align:'+cm.config[i].align+';';
33375             }
33376             var hidden = '';
33377             if(cm.isHidden(i)){
33378                 hidden = 'display:none;';
33379             }
33380             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33381             ruleBuf.push(
33382                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33383                     this.hdSelector, cid, " {\n", align, width, "}\n",
33384                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33385                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33386         }
33387         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33388     },
33389
33390     updateSplitters : function(){
33391         var cm = this.cm, s = this.getSplitters();
33392         if(s){ // splitters not created yet
33393             var pos = 0, locked = true;
33394             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33395                 if(cm.isHidden(i)) {
33396                     continue;
33397                 }
33398                 var w = cm.getColumnWidth(i); // make sure it's a number
33399                 if(!cm.isLocked(i) && locked){
33400                     pos = 0;
33401                     locked = false;
33402                 }
33403                 pos += w;
33404                 s[i].style.left = (pos-this.splitOffset) + "px";
33405             }
33406         }
33407     },
33408
33409     handleHiddenChange : function(colModel, colIndex, hidden){
33410         if(hidden){
33411             this.hideColumn(colIndex);
33412         }else{
33413             this.unhideColumn(colIndex);
33414         }
33415     },
33416
33417     hideColumn : function(colIndex){
33418         var cid = this.getColumnId(colIndex);
33419         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33420         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33421         if(Roo.isSafari){
33422             this.updateHeaders();
33423         }
33424         this.updateSplitters();
33425         this.layout();
33426     },
33427
33428     unhideColumn : function(colIndex){
33429         var cid = this.getColumnId(colIndex);
33430         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33431         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33432
33433         if(Roo.isSafari){
33434             this.updateHeaders();
33435         }
33436         this.updateSplitters();
33437         this.layout();
33438     },
33439
33440     insertRows : function(dm, firstRow, lastRow, isUpdate){
33441         if(firstRow == 0 && lastRow == dm.getCount()-1){
33442             this.refresh();
33443         }else{
33444             if(!isUpdate){
33445                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33446             }
33447             var s = this.getScrollState();
33448             var markup = this.renderRows(firstRow, lastRow);
33449             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33450             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33451             this.restoreScroll(s);
33452             if(!isUpdate){
33453                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33454                 this.syncRowHeights(firstRow, lastRow);
33455                 this.stripeRows(firstRow);
33456                 this.layout();
33457             }
33458         }
33459     },
33460
33461     bufferRows : function(markup, target, index){
33462         var before = null, trows = target.rows, tbody = target.tBodies[0];
33463         if(index < trows.length){
33464             before = trows[index];
33465         }
33466         var b = document.createElement("div");
33467         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33468         var rows = b.firstChild.rows;
33469         for(var i = 0, len = rows.length; i < len; i++){
33470             if(before){
33471                 tbody.insertBefore(rows[0], before);
33472             }else{
33473                 tbody.appendChild(rows[0]);
33474             }
33475         }
33476         b.innerHTML = "";
33477         b = null;
33478     },
33479
33480     deleteRows : function(dm, firstRow, lastRow){
33481         if(dm.getRowCount()<1){
33482             this.fireEvent("beforerefresh", this);
33483             this.mainBody.update("");
33484             this.lockedBody.update("");
33485             this.fireEvent("refresh", this);
33486         }else{
33487             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33488             var bt = this.getBodyTable();
33489             var tbody = bt.firstChild;
33490             var rows = bt.rows;
33491             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33492                 tbody.removeChild(rows[firstRow]);
33493             }
33494             this.stripeRows(firstRow);
33495             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33496         }
33497     },
33498
33499     updateRows : function(dataSource, firstRow, lastRow){
33500         var s = this.getScrollState();
33501         this.refresh();
33502         this.restoreScroll(s);
33503     },
33504
33505     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33506         if(!noRefresh){
33507            this.refresh();
33508         }
33509         this.updateHeaderSortState();
33510     },
33511
33512     getScrollState : function(){
33513         
33514         var sb = this.scroller.dom;
33515         return {left: sb.scrollLeft, top: sb.scrollTop};
33516     },
33517
33518     stripeRows : function(startRow){
33519         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33520             return;
33521         }
33522         startRow = startRow || 0;
33523         var rows = this.getBodyTable().rows;
33524         var lrows = this.getLockedTable().rows;
33525         var cls = ' x-grid-row-alt ';
33526         for(var i = startRow, len = rows.length; i < len; i++){
33527             var row = rows[i], lrow = lrows[i];
33528             var isAlt = ((i+1) % 2 == 0);
33529             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33530             if(isAlt == hasAlt){
33531                 continue;
33532             }
33533             if(isAlt){
33534                 row.className += " x-grid-row-alt";
33535             }else{
33536                 row.className = row.className.replace("x-grid-row-alt", "");
33537             }
33538             if(lrow){
33539                 lrow.className = row.className;
33540             }
33541         }
33542     },
33543
33544     restoreScroll : function(state){
33545         //Roo.log('GridView.restoreScroll');
33546         var sb = this.scroller.dom;
33547         sb.scrollLeft = state.left;
33548         sb.scrollTop = state.top;
33549         this.syncScroll();
33550     },
33551
33552     syncScroll : function(){
33553         //Roo.log('GridView.syncScroll');
33554         var sb = this.scroller.dom;
33555         var sh = this.mainHd.dom;
33556         var bs = this.mainBody.dom;
33557         var lv = this.lockedBody.dom;
33558         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33559         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33560     },
33561
33562     handleScroll : function(e){
33563         this.syncScroll();
33564         var sb = this.scroller.dom;
33565         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33566         e.stopEvent();
33567     },
33568
33569     handleWheel : function(e){
33570         var d = e.getWheelDelta();
33571         this.scroller.dom.scrollTop -= d*22;
33572         // set this here to prevent jumpy scrolling on large tables
33573         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33574         e.stopEvent();
33575     },
33576
33577     renderRows : function(startRow, endRow){
33578         // pull in all the crap needed to render rows
33579         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33580         var colCount = cm.getColumnCount();
33581
33582         if(ds.getCount() < 1){
33583             return ["", ""];
33584         }
33585
33586         // build a map for all the columns
33587         var cs = [];
33588         for(var i = 0; i < colCount; i++){
33589             var name = cm.getDataIndex(i);
33590             cs[i] = {
33591                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33592                 renderer : cm.getRenderer(i),
33593                 id : cm.getColumnId(i),
33594                 locked : cm.isLocked(i),
33595                 has_editor : cm.isCellEditable(i)
33596             };
33597         }
33598
33599         startRow = startRow || 0;
33600         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33601
33602         // records to render
33603         var rs = ds.getRange(startRow, endRow);
33604
33605         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33606     },
33607
33608     // As much as I hate to duplicate code, this was branched because FireFox really hates
33609     // [].join("") on strings. The performance difference was substantial enough to
33610     // branch this function
33611     doRender : Roo.isGecko ?
33612             function(cs, rs, ds, startRow, colCount, stripe){
33613                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33614                 // buffers
33615                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33616                 
33617                 var hasListener = this.grid.hasListener('rowclass');
33618                 var rowcfg = {};
33619                 for(var j = 0, len = rs.length; j < len; j++){
33620                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33621                     for(var i = 0; i < colCount; i++){
33622                         c = cs[i];
33623                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33624                         p.id = c.id;
33625                         p.css = p.attr = "";
33626                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33627                         if(p.value == undefined || p.value === "") {
33628                             p.value = "&#160;";
33629                         }
33630                         if(c.has_editor){
33631                             p.css += ' x-grid-editable-cell';
33632                         }
33633                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33634                             p.css +=  ' x-grid-dirty-cell';
33635                         }
33636                         var markup = ct.apply(p);
33637                         if(!c.locked){
33638                             cb+= markup;
33639                         }else{
33640                             lcb+= markup;
33641                         }
33642                     }
33643                     var alt = [];
33644                     if(stripe && ((rowIndex+1) % 2 == 0)){
33645                         alt.push("x-grid-row-alt")
33646                     }
33647                     if(r.dirty){
33648                         alt.push(  " x-grid-dirty-row");
33649                     }
33650                     rp.cells = lcb;
33651                     if(this.getRowClass){
33652                         alt.push(this.getRowClass(r, rowIndex));
33653                     }
33654                     if (hasListener) {
33655                         rowcfg = {
33656                              
33657                             record: r,
33658                             rowIndex : rowIndex,
33659                             rowClass : ''
33660                         };
33661                         this.grid.fireEvent('rowclass', this, rowcfg);
33662                         alt.push(rowcfg.rowClass);
33663                     }
33664                     rp.alt = alt.join(" ");
33665                     lbuf+= rt.apply(rp);
33666                     rp.cells = cb;
33667                     buf+=  rt.apply(rp);
33668                 }
33669                 return [lbuf, buf];
33670             } :
33671             function(cs, rs, ds, startRow, colCount, stripe){
33672                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33673                 // buffers
33674                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33675                 var hasListener = this.grid.hasListener('rowclass');
33676  
33677                 var rowcfg = {};
33678                 for(var j = 0, len = rs.length; j < len; j++){
33679                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33680                     for(var i = 0; i < colCount; i++){
33681                         c = cs[i];
33682                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33683                         p.id = c.id;
33684                         p.css = p.attr = "";
33685                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33686                         if(p.value == undefined || p.value === "") {
33687                             p.value = "&#160;";
33688                         }
33689                         //Roo.log(c);
33690                          if(c.has_editor){
33691                             p.css += ' x-grid-editable-cell';
33692                         }
33693                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33694                             p.css += ' x-grid-dirty-cell' 
33695                         }
33696                         
33697                         var markup = ct.apply(p);
33698                         if(!c.locked){
33699                             cb[cb.length] = markup;
33700                         }else{
33701                             lcb[lcb.length] = markup;
33702                         }
33703                     }
33704                     var alt = [];
33705                     if(stripe && ((rowIndex+1) % 2 == 0)){
33706                         alt.push( "x-grid-row-alt");
33707                     }
33708                     if(r.dirty){
33709                         alt.push(" x-grid-dirty-row");
33710                     }
33711                     rp.cells = lcb;
33712                     if(this.getRowClass){
33713                         alt.push( this.getRowClass(r, rowIndex));
33714                     }
33715                     if (hasListener) {
33716                         rowcfg = {
33717                              
33718                             record: r,
33719                             rowIndex : rowIndex,
33720                             rowClass : ''
33721                         };
33722                         this.grid.fireEvent('rowclass', this, rowcfg);
33723                         alt.push(rowcfg.rowClass);
33724                     }
33725                     
33726                     rp.alt = alt.join(" ");
33727                     rp.cells = lcb.join("");
33728                     lbuf[lbuf.length] = rt.apply(rp);
33729                     rp.cells = cb.join("");
33730                     buf[buf.length] =  rt.apply(rp);
33731                 }
33732                 return [lbuf.join(""), buf.join("")];
33733             },
33734
33735     renderBody : function(){
33736         var markup = this.renderRows();
33737         var bt = this.templates.body;
33738         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33739     },
33740
33741     /**
33742      * Refreshes the grid
33743      * @param {Boolean} headersToo
33744      */
33745     refresh : function(headersToo){
33746         this.fireEvent("beforerefresh", this);
33747         this.grid.stopEditing();
33748         var result = this.renderBody();
33749         this.lockedBody.update(result[0]);
33750         this.mainBody.update(result[1]);
33751         if(headersToo === true){
33752             this.updateHeaders();
33753             this.updateColumns();
33754             this.updateSplitters();
33755             this.updateHeaderSortState();
33756         }
33757         this.syncRowHeights();
33758         this.layout();
33759         this.fireEvent("refresh", this);
33760     },
33761
33762     handleColumnMove : function(cm, oldIndex, newIndex){
33763         this.indexMap = null;
33764         var s = this.getScrollState();
33765         this.refresh(true);
33766         this.restoreScroll(s);
33767         this.afterMove(newIndex);
33768     },
33769
33770     afterMove : function(colIndex){
33771         if(this.enableMoveAnim && Roo.enableFx){
33772             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33773         }
33774         // if multisort - fix sortOrder, and reload..
33775         if (this.grid.dataSource.multiSort) {
33776             // the we can call sort again..
33777             var dm = this.grid.dataSource;
33778             var cm = this.grid.colModel;
33779             var so = [];
33780             for(var i = 0; i < cm.config.length; i++ ) {
33781                 
33782                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33783                     continue; // dont' bother, it's not in sort list or being set.
33784                 }
33785                 
33786                 so.push(cm.config[i].dataIndex);
33787             };
33788             dm.sortOrder = so;
33789             dm.load(dm.lastOptions);
33790             
33791             
33792         }
33793         
33794     },
33795
33796     updateCell : function(dm, rowIndex, dataIndex){
33797         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33798         if(typeof colIndex == "undefined"){ // not present in grid
33799             return;
33800         }
33801         var cm = this.grid.colModel;
33802         var cell = this.getCell(rowIndex, colIndex);
33803         var cellText = this.getCellText(rowIndex, colIndex);
33804
33805         var p = {
33806             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33807             id : cm.getColumnId(colIndex),
33808             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33809         };
33810         var renderer = cm.getRenderer(colIndex);
33811         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33812         if(typeof val == "undefined" || val === "") {
33813             val = "&#160;";
33814         }
33815         cellText.innerHTML = val;
33816         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33817         this.syncRowHeights(rowIndex, rowIndex);
33818     },
33819
33820     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33821         var maxWidth = 0;
33822         if(this.grid.autoSizeHeaders){
33823             var h = this.getHeaderCellMeasure(colIndex);
33824             maxWidth = Math.max(maxWidth, h.scrollWidth);
33825         }
33826         var tb, index;
33827         if(this.cm.isLocked(colIndex)){
33828             tb = this.getLockedTable();
33829             index = colIndex;
33830         }else{
33831             tb = this.getBodyTable();
33832             index = colIndex - this.cm.getLockedCount();
33833         }
33834         if(tb && tb.rows){
33835             var rows = tb.rows;
33836             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33837             for(var i = 0; i < stopIndex; i++){
33838                 var cell = rows[i].childNodes[index].firstChild;
33839                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33840             }
33841         }
33842         return maxWidth + /*margin for error in IE*/ 5;
33843     },
33844     /**
33845      * Autofit a column to its content.
33846      * @param {Number} colIndex
33847      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33848      */
33849      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33850          if(this.cm.isHidden(colIndex)){
33851              return; // can't calc a hidden column
33852          }
33853         if(forceMinSize){
33854             var cid = this.cm.getColumnId(colIndex);
33855             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33856            if(this.grid.autoSizeHeaders){
33857                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33858            }
33859         }
33860         var newWidth = this.calcColumnWidth(colIndex);
33861         this.cm.setColumnWidth(colIndex,
33862             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33863         if(!suppressEvent){
33864             this.grid.fireEvent("columnresize", colIndex, newWidth);
33865         }
33866     },
33867
33868     /**
33869      * Autofits all columns to their content and then expands to fit any extra space in the grid
33870      */
33871      autoSizeColumns : function(){
33872         var cm = this.grid.colModel;
33873         var colCount = cm.getColumnCount();
33874         for(var i = 0; i < colCount; i++){
33875             this.autoSizeColumn(i, true, true);
33876         }
33877         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33878             this.fitColumns();
33879         }else{
33880             this.updateColumns();
33881             this.layout();
33882         }
33883     },
33884
33885     /**
33886      * Autofits all columns to the grid's width proportionate with their current size
33887      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33888      */
33889     fitColumns : function(reserveScrollSpace){
33890         var cm = this.grid.colModel;
33891         var colCount = cm.getColumnCount();
33892         var cols = [];
33893         var width = 0;
33894         var i, w;
33895         for (i = 0; i < colCount; i++){
33896             if(!cm.isHidden(i) && !cm.isFixed(i)){
33897                 w = cm.getColumnWidth(i);
33898                 cols.push(i);
33899                 cols.push(w);
33900                 width += w;
33901             }
33902         }
33903         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33904         if(reserveScrollSpace){
33905             avail -= 17;
33906         }
33907         var frac = (avail - cm.getTotalWidth())/width;
33908         while (cols.length){
33909             w = cols.pop();
33910             i = cols.pop();
33911             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33912         }
33913         this.updateColumns();
33914         this.layout();
33915     },
33916
33917     onRowSelect : function(rowIndex){
33918         var row = this.getRowComposite(rowIndex);
33919         row.addClass("x-grid-row-selected");
33920     },
33921
33922     onRowDeselect : function(rowIndex){
33923         var row = this.getRowComposite(rowIndex);
33924         row.removeClass("x-grid-row-selected");
33925     },
33926
33927     onCellSelect : function(row, col){
33928         var cell = this.getCell(row, col);
33929         if(cell){
33930             Roo.fly(cell).addClass("x-grid-cell-selected");
33931         }
33932     },
33933
33934     onCellDeselect : function(row, col){
33935         var cell = this.getCell(row, col);
33936         if(cell){
33937             Roo.fly(cell).removeClass("x-grid-cell-selected");
33938         }
33939     },
33940
33941     updateHeaderSortState : function(){
33942         
33943         // sort state can be single { field: xxx, direction : yyy}
33944         // or   { xxx=>ASC , yyy : DESC ..... }
33945         
33946         var mstate = {};
33947         if (!this.ds.multiSort) { 
33948             var state = this.ds.getSortState();
33949             if(!state){
33950                 return;
33951             }
33952             mstate[state.field] = state.direction;
33953             // FIXME... - this is not used here.. but might be elsewhere..
33954             this.sortState = state;
33955             
33956         } else {
33957             mstate = this.ds.sortToggle;
33958         }
33959         //remove existing sort classes..
33960         
33961         var sc = this.sortClasses;
33962         var hds = this.el.select(this.headerSelector).removeClass(sc);
33963         
33964         for(var f in mstate) {
33965         
33966             var sortColumn = this.cm.findColumnIndex(f);
33967             
33968             if(sortColumn != -1){
33969                 var sortDir = mstate[f];        
33970                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33971             }
33972         }
33973         
33974          
33975         
33976     },
33977
33978
33979     handleHeaderClick : function(g, index,e){
33980         
33981         Roo.log("header click");
33982         
33983         if (Roo.isTouch) {
33984             // touch events on header are handled by context
33985             this.handleHdCtx(g,index,e);
33986             return;
33987         }
33988         
33989         
33990         if(this.headersDisabled){
33991             return;
33992         }
33993         var dm = g.dataSource, cm = g.colModel;
33994         if(!cm.isSortable(index)){
33995             return;
33996         }
33997         g.stopEditing();
33998         
33999         if (dm.multiSort) {
34000             // update the sortOrder
34001             var so = [];
34002             for(var i = 0; i < cm.config.length; i++ ) {
34003                 
34004                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34005                     continue; // dont' bother, it's not in sort list or being set.
34006                 }
34007                 
34008                 so.push(cm.config[i].dataIndex);
34009             };
34010             dm.sortOrder = so;
34011         }
34012         
34013         
34014         dm.sort(cm.getDataIndex(index));
34015     },
34016
34017
34018     destroy : function(){
34019         if(this.colMenu){
34020             this.colMenu.removeAll();
34021             Roo.menu.MenuMgr.unregister(this.colMenu);
34022             this.colMenu.getEl().remove();
34023             delete this.colMenu;
34024         }
34025         if(this.hmenu){
34026             this.hmenu.removeAll();
34027             Roo.menu.MenuMgr.unregister(this.hmenu);
34028             this.hmenu.getEl().remove();
34029             delete this.hmenu;
34030         }
34031         if(this.grid.enableColumnMove){
34032             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34033             if(dds){
34034                 for(var dd in dds){
34035                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34036                         var elid = dds[dd].dragElId;
34037                         dds[dd].unreg();
34038                         Roo.get(elid).remove();
34039                     } else if(dds[dd].config.isTarget){
34040                         dds[dd].proxyTop.remove();
34041                         dds[dd].proxyBottom.remove();
34042                         dds[dd].unreg();
34043                     }
34044                     if(Roo.dd.DDM.locationCache[dd]){
34045                         delete Roo.dd.DDM.locationCache[dd];
34046                     }
34047                 }
34048                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34049             }
34050         }
34051         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34052         this.bind(null, null);
34053         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34054     },
34055
34056     handleLockChange : function(){
34057         this.refresh(true);
34058     },
34059
34060     onDenyColumnLock : function(){
34061
34062     },
34063
34064     onDenyColumnHide : function(){
34065
34066     },
34067
34068     handleHdMenuClick : function(item){
34069         var index = this.hdCtxIndex;
34070         var cm = this.cm, ds = this.ds;
34071         switch(item.id){
34072             case "asc":
34073                 ds.sort(cm.getDataIndex(index), "ASC");
34074                 break;
34075             case "desc":
34076                 ds.sort(cm.getDataIndex(index), "DESC");
34077                 break;
34078             case "lock":
34079                 var lc = cm.getLockedCount();
34080                 if(cm.getColumnCount(true) <= lc+1){
34081                     this.onDenyColumnLock();
34082                     return;
34083                 }
34084                 if(lc != index){
34085                     cm.setLocked(index, true, true);
34086                     cm.moveColumn(index, lc);
34087                     this.grid.fireEvent("columnmove", index, lc);
34088                 }else{
34089                     cm.setLocked(index, true);
34090                 }
34091             break;
34092             case "unlock":
34093                 var lc = cm.getLockedCount();
34094                 if((lc-1) != index){
34095                     cm.setLocked(index, false, true);
34096                     cm.moveColumn(index, lc-1);
34097                     this.grid.fireEvent("columnmove", index, lc-1);
34098                 }else{
34099                     cm.setLocked(index, false);
34100                 }
34101             break;
34102             case 'wider': // used to expand cols on touch..
34103             case 'narrow':
34104                 var cw = cm.getColumnWidth(index);
34105                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34106                 cw = Math.max(0, cw);
34107                 cw = Math.min(cw,4000);
34108                 cm.setColumnWidth(index, cw);
34109                 break;
34110                 
34111             default:
34112                 index = cm.getIndexById(item.id.substr(4));
34113                 if(index != -1){
34114                     if(item.checked && cm.getColumnCount(true) <= 1){
34115                         this.onDenyColumnHide();
34116                         return false;
34117                     }
34118                     cm.setHidden(index, item.checked);
34119                 }
34120         }
34121         return true;
34122     },
34123
34124     beforeColMenuShow : function(){
34125         var cm = this.cm,  colCount = cm.getColumnCount();
34126         this.colMenu.removeAll();
34127         for(var i = 0; i < colCount; i++){
34128             this.colMenu.add(new Roo.menu.CheckItem({
34129                 id: "col-"+cm.getColumnId(i),
34130                 text: cm.getColumnHeader(i),
34131                 checked: !cm.isHidden(i),
34132                 hideOnClick:false
34133             }));
34134         }
34135     },
34136
34137     handleHdCtx : function(g, index, e){
34138         e.stopEvent();
34139         var hd = this.getHeaderCell(index);
34140         this.hdCtxIndex = index;
34141         var ms = this.hmenu.items, cm = this.cm;
34142         ms.get("asc").setDisabled(!cm.isSortable(index));
34143         ms.get("desc").setDisabled(!cm.isSortable(index));
34144         if(this.grid.enableColLock !== false){
34145             ms.get("lock").setDisabled(cm.isLocked(index));
34146             ms.get("unlock").setDisabled(!cm.isLocked(index));
34147         }
34148         this.hmenu.show(hd, "tl-bl");
34149     },
34150
34151     handleHdOver : function(e){
34152         var hd = this.findHeaderCell(e.getTarget());
34153         if(hd && !this.headersDisabled){
34154             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34155                this.fly(hd).addClass("x-grid-hd-over");
34156             }
34157         }
34158     },
34159
34160     handleHdOut : function(e){
34161         var hd = this.findHeaderCell(e.getTarget());
34162         if(hd){
34163             this.fly(hd).removeClass("x-grid-hd-over");
34164         }
34165     },
34166
34167     handleSplitDblClick : function(e, t){
34168         var i = this.getCellIndex(t);
34169         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34170             this.autoSizeColumn(i, true);
34171             this.layout();
34172         }
34173     },
34174
34175     render : function(){
34176
34177         var cm = this.cm;
34178         var colCount = cm.getColumnCount();
34179
34180         if(this.grid.monitorWindowResize === true){
34181             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34182         }
34183         var header = this.renderHeaders();
34184         var body = this.templates.body.apply({rows:""});
34185         var html = this.templates.master.apply({
34186             lockedBody: body,
34187             body: body,
34188             lockedHeader: header[0],
34189             header: header[1]
34190         });
34191
34192         //this.updateColumns();
34193
34194         this.grid.getGridEl().dom.innerHTML = html;
34195
34196         this.initElements();
34197         
34198         // a kludge to fix the random scolling effect in webkit
34199         this.el.on("scroll", function() {
34200             this.el.dom.scrollTop=0; // hopefully not recursive..
34201         },this);
34202
34203         this.scroller.on("scroll", this.handleScroll, this);
34204         this.lockedBody.on("mousewheel", this.handleWheel, this);
34205         this.mainBody.on("mousewheel", this.handleWheel, this);
34206
34207         this.mainHd.on("mouseover", this.handleHdOver, this);
34208         this.mainHd.on("mouseout", this.handleHdOut, this);
34209         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34210                 {delegate: "."+this.splitClass});
34211
34212         this.lockedHd.on("mouseover", this.handleHdOver, this);
34213         this.lockedHd.on("mouseout", this.handleHdOut, this);
34214         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34215                 {delegate: "."+this.splitClass});
34216
34217         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34218             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34219         }
34220
34221         this.updateSplitters();
34222
34223         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34224             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34225             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34226         }
34227
34228         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34229             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34230             this.hmenu.add(
34231                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34232                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34233             );
34234             if(this.grid.enableColLock !== false){
34235                 this.hmenu.add('-',
34236                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34237                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34238                 );
34239             }
34240             if (Roo.isTouch) {
34241                  this.hmenu.add('-',
34242                     {id:"wider", text: this.columnsWiderText},
34243                     {id:"narrow", text: this.columnsNarrowText }
34244                 );
34245                 
34246                  
34247             }
34248             
34249             if(this.grid.enableColumnHide !== false){
34250
34251                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34252                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34253                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34254
34255                 this.hmenu.add('-',
34256                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34257                 );
34258             }
34259             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34260
34261             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34262         }
34263
34264         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34265             this.dd = new Roo.grid.GridDragZone(this.grid, {
34266                 ddGroup : this.grid.ddGroup || 'GridDD'
34267             });
34268             
34269         }
34270
34271         /*
34272         for(var i = 0; i < colCount; i++){
34273             if(cm.isHidden(i)){
34274                 this.hideColumn(i);
34275             }
34276             if(cm.config[i].align){
34277                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34278                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34279             }
34280         }*/
34281         
34282         this.updateHeaderSortState();
34283
34284         this.beforeInitialResize();
34285         this.layout(true);
34286
34287         // two part rendering gives faster view to the user
34288         this.renderPhase2.defer(1, this);
34289     },
34290
34291     renderPhase2 : function(){
34292         // render the rows now
34293         this.refresh();
34294         if(this.grid.autoSizeColumns){
34295             this.autoSizeColumns();
34296         }
34297     },
34298
34299     beforeInitialResize : function(){
34300
34301     },
34302
34303     onColumnSplitterMoved : function(i, w){
34304         this.userResized = true;
34305         var cm = this.grid.colModel;
34306         cm.setColumnWidth(i, w, true);
34307         var cid = cm.getColumnId(i);
34308         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34309         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34310         this.updateSplitters();
34311         this.layout();
34312         this.grid.fireEvent("columnresize", i, w);
34313     },
34314
34315     syncRowHeights : function(startIndex, endIndex){
34316         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34317             startIndex = startIndex || 0;
34318             var mrows = this.getBodyTable().rows;
34319             var lrows = this.getLockedTable().rows;
34320             var len = mrows.length-1;
34321             endIndex = Math.min(endIndex || len, len);
34322             for(var i = startIndex; i <= endIndex; i++){
34323                 var m = mrows[i], l = lrows[i];
34324                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34325                 m.style.height = l.style.height = h + "px";
34326             }
34327         }
34328     },
34329
34330     layout : function(initialRender, is2ndPass){
34331         var g = this.grid;
34332         var auto = g.autoHeight;
34333         var scrollOffset = 16;
34334         var c = g.getGridEl(), cm = this.cm,
34335                 expandCol = g.autoExpandColumn,
34336                 gv = this;
34337         //c.beginMeasure();
34338
34339         if(!c.dom.offsetWidth){ // display:none?
34340             if(initialRender){
34341                 this.lockedWrap.show();
34342                 this.mainWrap.show();
34343             }
34344             return;
34345         }
34346
34347         var hasLock = this.cm.isLocked(0);
34348
34349         var tbh = this.headerPanel.getHeight();
34350         var bbh = this.footerPanel.getHeight();
34351
34352         if(auto){
34353             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34354             var newHeight = ch + c.getBorderWidth("tb");
34355             if(g.maxHeight){
34356                 newHeight = Math.min(g.maxHeight, newHeight);
34357             }
34358             c.setHeight(newHeight);
34359         }
34360
34361         if(g.autoWidth){
34362             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34363         }
34364
34365         var s = this.scroller;
34366
34367         var csize = c.getSize(true);
34368
34369         this.el.setSize(csize.width, csize.height);
34370
34371         this.headerPanel.setWidth(csize.width);
34372         this.footerPanel.setWidth(csize.width);
34373
34374         var hdHeight = this.mainHd.getHeight();
34375         var vw = csize.width;
34376         var vh = csize.height - (tbh + bbh);
34377
34378         s.setSize(vw, vh);
34379
34380         var bt = this.getBodyTable();
34381         
34382         if(cm.getLockedCount() == cm.config.length){
34383             bt = this.getLockedTable();
34384         }
34385         
34386         var ltWidth = hasLock ?
34387                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34388
34389         var scrollHeight = bt.offsetHeight;
34390         var scrollWidth = ltWidth + bt.offsetWidth;
34391         var vscroll = false, hscroll = false;
34392
34393         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34394
34395         var lw = this.lockedWrap, mw = this.mainWrap;
34396         var lb = this.lockedBody, mb = this.mainBody;
34397
34398         setTimeout(function(){
34399             var t = s.dom.offsetTop;
34400             var w = s.dom.clientWidth,
34401                 h = s.dom.clientHeight;
34402
34403             lw.setTop(t);
34404             lw.setSize(ltWidth, h);
34405
34406             mw.setLeftTop(ltWidth, t);
34407             mw.setSize(w-ltWidth, h);
34408
34409             lb.setHeight(h-hdHeight);
34410             mb.setHeight(h-hdHeight);
34411
34412             if(is2ndPass !== true && !gv.userResized && expandCol){
34413                 // high speed resize without full column calculation
34414                 
34415                 var ci = cm.getIndexById(expandCol);
34416                 if (ci < 0) {
34417                     ci = cm.findColumnIndex(expandCol);
34418                 }
34419                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34420                 var expandId = cm.getColumnId(ci);
34421                 var  tw = cm.getTotalWidth(false);
34422                 var currentWidth = cm.getColumnWidth(ci);
34423                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34424                 if(currentWidth != cw){
34425                     cm.setColumnWidth(ci, cw, true);
34426                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34427                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34428                     gv.updateSplitters();
34429                     gv.layout(false, true);
34430                 }
34431             }
34432
34433             if(initialRender){
34434                 lw.show();
34435                 mw.show();
34436             }
34437             //c.endMeasure();
34438         }, 10);
34439     },
34440
34441     onWindowResize : function(){
34442         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34443             return;
34444         }
34445         this.layout();
34446     },
34447
34448     appendFooter : function(parentEl){
34449         return null;
34450     },
34451
34452     sortAscText : "Sort Ascending",
34453     sortDescText : "Sort Descending",
34454     lockText : "Lock Column",
34455     unlockText : "Unlock Column",
34456     columnsText : "Columns",
34457  
34458     columnsWiderText : "Wider",
34459     columnsNarrowText : "Thinner"
34460 });
34461
34462
34463 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34464     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34465     this.proxy.el.addClass('x-grid3-col-dd');
34466 };
34467
34468 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34469     handleMouseDown : function(e){
34470
34471     },
34472
34473     callHandleMouseDown : function(e){
34474         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34475     }
34476 });
34477 /*
34478  * Based on:
34479  * Ext JS Library 1.1.1
34480  * Copyright(c) 2006-2007, Ext JS, LLC.
34481  *
34482  * Originally Released Under LGPL - original licence link has changed is not relivant.
34483  *
34484  * Fork - LGPL
34485  * <script type="text/javascript">
34486  */
34487  
34488 // private
34489 // This is a support class used internally by the Grid components
34490 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34491     this.grid = grid;
34492     this.view = grid.getView();
34493     this.proxy = this.view.resizeProxy;
34494     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34495         "gridSplitters" + this.grid.getGridEl().id, {
34496         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34497     });
34498     this.setHandleElId(Roo.id(hd));
34499     this.setOuterHandleElId(Roo.id(hd2));
34500     this.scroll = false;
34501 };
34502 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34503     fly: Roo.Element.fly,
34504
34505     b4StartDrag : function(x, y){
34506         this.view.headersDisabled = true;
34507         this.proxy.setHeight(this.view.mainWrap.getHeight());
34508         var w = this.cm.getColumnWidth(this.cellIndex);
34509         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34510         this.resetConstraints();
34511         this.setXConstraint(minw, 1000);
34512         this.setYConstraint(0, 0);
34513         this.minX = x - minw;
34514         this.maxX = x + 1000;
34515         this.startPos = x;
34516         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34517     },
34518
34519
34520     handleMouseDown : function(e){
34521         ev = Roo.EventObject.setEvent(e);
34522         var t = this.fly(ev.getTarget());
34523         if(t.hasClass("x-grid-split")){
34524             this.cellIndex = this.view.getCellIndex(t.dom);
34525             this.split = t.dom;
34526             this.cm = this.grid.colModel;
34527             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34528                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34529             }
34530         }
34531     },
34532
34533     endDrag : function(e){
34534         this.view.headersDisabled = false;
34535         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34536         var diff = endX - this.startPos;
34537         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34538     },
34539
34540     autoOffset : function(){
34541         this.setDelta(0,0);
34542     }
34543 });/*
34544  * Based on:
34545  * Ext JS Library 1.1.1
34546  * Copyright(c) 2006-2007, Ext JS, LLC.
34547  *
34548  * Originally Released Under LGPL - original licence link has changed is not relivant.
34549  *
34550  * Fork - LGPL
34551  * <script type="text/javascript">
34552  */
34553  
34554 // private
34555 // This is a support class used internally by the Grid components
34556 Roo.grid.GridDragZone = function(grid, config){
34557     this.view = grid.getView();
34558     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34559     if(this.view.lockedBody){
34560         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34561         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34562     }
34563     this.scroll = false;
34564     this.grid = grid;
34565     this.ddel = document.createElement('div');
34566     this.ddel.className = 'x-grid-dd-wrap';
34567 };
34568
34569 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34570     ddGroup : "GridDD",
34571
34572     getDragData : function(e){
34573         var t = Roo.lib.Event.getTarget(e);
34574         var rowIndex = this.view.findRowIndex(t);
34575         var sm = this.grid.selModel;
34576             
34577         //Roo.log(rowIndex);
34578         
34579         if (sm.getSelectedCell) {
34580             // cell selection..
34581             if (!sm.getSelectedCell()) {
34582                 return false;
34583             }
34584             if (rowIndex != sm.getSelectedCell()[0]) {
34585                 return false;
34586             }
34587         
34588         }
34589         
34590         if(rowIndex !== false){
34591             
34592             // if editorgrid.. 
34593             
34594             
34595             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34596                
34597             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34598               //  
34599             //}
34600             if (e.hasModifier()){
34601                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34602             }
34603             
34604             Roo.log("getDragData");
34605             
34606             return {
34607                 grid: this.grid,
34608                 ddel: this.ddel,
34609                 rowIndex: rowIndex,
34610                 selections:sm.getSelections ? sm.getSelections() : (
34611                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34612                 )
34613             };
34614         }
34615         return false;
34616     },
34617
34618     onInitDrag : function(e){
34619         var data = this.dragData;
34620         this.ddel.innerHTML = this.grid.getDragDropText();
34621         this.proxy.update(this.ddel);
34622         // fire start drag?
34623     },
34624
34625     afterRepair : function(){
34626         this.dragging = false;
34627     },
34628
34629     getRepairXY : function(e, data){
34630         return false;
34631     },
34632
34633     onEndDrag : function(data, e){
34634         // fire end drag?
34635     },
34636
34637     onValidDrop : function(dd, e, id){
34638         // fire drag drop?
34639         this.hideProxy();
34640     },
34641
34642     beforeInvalidDrop : function(e, id){
34643
34644     }
34645 });/*
34646  * Based on:
34647  * Ext JS Library 1.1.1
34648  * Copyright(c) 2006-2007, Ext JS, LLC.
34649  *
34650  * Originally Released Under LGPL - original licence link has changed is not relivant.
34651  *
34652  * Fork - LGPL
34653  * <script type="text/javascript">
34654  */
34655  
34656
34657 /**
34658  * @class Roo.grid.ColumnModel
34659  * @extends Roo.util.Observable
34660  * This is the default implementation of a ColumnModel used by the Grid. It defines
34661  * the columns in the grid.
34662  * <br>Usage:<br>
34663  <pre><code>
34664  var colModel = new Roo.grid.ColumnModel([
34665         {header: "Ticker", width: 60, sortable: true, locked: true},
34666         {header: "Company Name", width: 150, sortable: true},
34667         {header: "Market Cap.", width: 100, sortable: true},
34668         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34669         {header: "Employees", width: 100, sortable: true, resizable: false}
34670  ]);
34671  </code></pre>
34672  * <p>
34673  
34674  * The config options listed for this class are options which may appear in each
34675  * individual column definition.
34676  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34677  * @constructor
34678  * @param {Object} config An Array of column config objects. See this class's
34679  * config objects for details.
34680 */
34681 Roo.grid.ColumnModel = function(config){
34682         /**
34683      * The config passed into the constructor
34684      */
34685     this.config = config;
34686     this.lookup = {};
34687
34688     // if no id, create one
34689     // if the column does not have a dataIndex mapping,
34690     // map it to the order it is in the config
34691     for(var i = 0, len = config.length; i < len; i++){
34692         var c = config[i];
34693         if(typeof c.dataIndex == "undefined"){
34694             c.dataIndex = i;
34695         }
34696         if(typeof c.renderer == "string"){
34697             c.renderer = Roo.util.Format[c.renderer];
34698         }
34699         if(typeof c.id == "undefined"){
34700             c.id = Roo.id();
34701         }
34702         if(c.editor && c.editor.xtype){
34703             c.editor  = Roo.factory(c.editor, Roo.grid);
34704         }
34705         if(c.editor && c.editor.isFormField){
34706             c.editor = new Roo.grid.GridEditor(c.editor);
34707         }
34708         this.lookup[c.id] = c;
34709     }
34710
34711     /**
34712      * The width of columns which have no width specified (defaults to 100)
34713      * @type Number
34714      */
34715     this.defaultWidth = 100;
34716
34717     /**
34718      * Default sortable of columns which have no sortable specified (defaults to false)
34719      * @type Boolean
34720      */
34721     this.defaultSortable = false;
34722
34723     this.addEvents({
34724         /**
34725              * @event widthchange
34726              * Fires when the width of a column changes.
34727              * @param {ColumnModel} this
34728              * @param {Number} columnIndex The column index
34729              * @param {Number} newWidth The new width
34730              */
34731             "widthchange": true,
34732         /**
34733              * @event headerchange
34734              * Fires when the text of a header changes.
34735              * @param {ColumnModel} this
34736              * @param {Number} columnIndex The column index
34737              * @param {Number} newText The new header text
34738              */
34739             "headerchange": true,
34740         /**
34741              * @event hiddenchange
34742              * Fires when a column is hidden or "unhidden".
34743              * @param {ColumnModel} this
34744              * @param {Number} columnIndex The column index
34745              * @param {Boolean} hidden true if hidden, false otherwise
34746              */
34747             "hiddenchange": true,
34748             /**
34749          * @event columnmoved
34750          * Fires when a column is moved.
34751          * @param {ColumnModel} this
34752          * @param {Number} oldIndex
34753          * @param {Number} newIndex
34754          */
34755         "columnmoved" : true,
34756         /**
34757          * @event columlockchange
34758          * Fires when a column's locked state is changed
34759          * @param {ColumnModel} this
34760          * @param {Number} colIndex
34761          * @param {Boolean} locked true if locked
34762          */
34763         "columnlockchange" : true
34764     });
34765     Roo.grid.ColumnModel.superclass.constructor.call(this);
34766 };
34767 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34768     /**
34769      * @cfg {String} header The header text to display in the Grid view.
34770      */
34771     /**
34772      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34773      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34774      * specified, the column's index is used as an index into the Record's data Array.
34775      */
34776     /**
34777      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34778      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34779      */
34780     /**
34781      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34782      * Defaults to the value of the {@link #defaultSortable} property.
34783      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34784      */
34785     /**
34786      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34787      */
34788     /**
34789      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34790      */
34791     /**
34792      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34793      */
34794     /**
34795      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34796      */
34797     /**
34798      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34799      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34800      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34801      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34802      */
34803        /**
34804      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34805      */
34806     /**
34807      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34808      */
34809     /**
34810      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34811      */
34812     /**
34813      * @cfg {String} cursor (Optional)
34814      */
34815     /**
34816      * @cfg {String} tooltip (Optional)
34817      */
34818     /**
34819      * @cfg {Number} xs (Optional)
34820      */
34821     /**
34822      * @cfg {Number} sm (Optional)
34823      */
34824     /**
34825      * @cfg {Number} md (Optional)
34826      */
34827     /**
34828      * @cfg {Number} lg (Optional)
34829      */
34830     /**
34831      * Returns the id of the column at the specified index.
34832      * @param {Number} index The column index
34833      * @return {String} the id
34834      */
34835     getColumnId : function(index){
34836         return this.config[index].id;
34837     },
34838
34839     /**
34840      * Returns the column for a specified id.
34841      * @param {String} id The column id
34842      * @return {Object} the column
34843      */
34844     getColumnById : function(id){
34845         return this.lookup[id];
34846     },
34847
34848     
34849     /**
34850      * Returns the column for a specified dataIndex.
34851      * @param {String} dataIndex The column dataIndex
34852      * @return {Object|Boolean} the column or false if not found
34853      */
34854     getColumnByDataIndex: function(dataIndex){
34855         var index = this.findColumnIndex(dataIndex);
34856         return index > -1 ? this.config[index] : false;
34857     },
34858     
34859     /**
34860      * Returns the index for a specified column id.
34861      * @param {String} id The column id
34862      * @return {Number} the index, or -1 if not found
34863      */
34864     getIndexById : function(id){
34865         for(var i = 0, len = this.config.length; i < len; i++){
34866             if(this.config[i].id == id){
34867                 return i;
34868             }
34869         }
34870         return -1;
34871     },
34872     
34873     /**
34874      * Returns the index for a specified column dataIndex.
34875      * @param {String} dataIndex The column dataIndex
34876      * @return {Number} the index, or -1 if not found
34877      */
34878     
34879     findColumnIndex : function(dataIndex){
34880         for(var i = 0, len = this.config.length; i < len; i++){
34881             if(this.config[i].dataIndex == dataIndex){
34882                 return i;
34883             }
34884         }
34885         return -1;
34886     },
34887     
34888     
34889     moveColumn : function(oldIndex, newIndex){
34890         var c = this.config[oldIndex];
34891         this.config.splice(oldIndex, 1);
34892         this.config.splice(newIndex, 0, c);
34893         this.dataMap = null;
34894         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34895     },
34896
34897     isLocked : function(colIndex){
34898         return this.config[colIndex].locked === true;
34899     },
34900
34901     setLocked : function(colIndex, value, suppressEvent){
34902         if(this.isLocked(colIndex) == value){
34903             return;
34904         }
34905         this.config[colIndex].locked = value;
34906         if(!suppressEvent){
34907             this.fireEvent("columnlockchange", this, colIndex, value);
34908         }
34909     },
34910
34911     getTotalLockedWidth : function(){
34912         var totalWidth = 0;
34913         for(var i = 0; i < this.config.length; i++){
34914             if(this.isLocked(i) && !this.isHidden(i)){
34915                 this.totalWidth += this.getColumnWidth(i);
34916             }
34917         }
34918         return totalWidth;
34919     },
34920
34921     getLockedCount : function(){
34922         for(var i = 0, len = this.config.length; i < len; i++){
34923             if(!this.isLocked(i)){
34924                 return i;
34925             }
34926         }
34927         
34928         return this.config.length;
34929     },
34930
34931     /**
34932      * Returns the number of columns.
34933      * @return {Number}
34934      */
34935     getColumnCount : function(visibleOnly){
34936         if(visibleOnly === true){
34937             var c = 0;
34938             for(var i = 0, len = this.config.length; i < len; i++){
34939                 if(!this.isHidden(i)){
34940                     c++;
34941                 }
34942             }
34943             return c;
34944         }
34945         return this.config.length;
34946     },
34947
34948     /**
34949      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34950      * @param {Function} fn
34951      * @param {Object} scope (optional)
34952      * @return {Array} result
34953      */
34954     getColumnsBy : function(fn, scope){
34955         var r = [];
34956         for(var i = 0, len = this.config.length; i < len; i++){
34957             var c = this.config[i];
34958             if(fn.call(scope||this, c, i) === true){
34959                 r[r.length] = c;
34960             }
34961         }
34962         return r;
34963     },
34964
34965     /**
34966      * Returns true if the specified column is sortable.
34967      * @param {Number} col The column index
34968      * @return {Boolean}
34969      */
34970     isSortable : function(col){
34971         if(typeof this.config[col].sortable == "undefined"){
34972             return this.defaultSortable;
34973         }
34974         return this.config[col].sortable;
34975     },
34976
34977     /**
34978      * Returns the rendering (formatting) function defined for the column.
34979      * @param {Number} col The column index.
34980      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34981      */
34982     getRenderer : function(col){
34983         if(!this.config[col].renderer){
34984             return Roo.grid.ColumnModel.defaultRenderer;
34985         }
34986         return this.config[col].renderer;
34987     },
34988
34989     /**
34990      * Sets the rendering (formatting) function for a column.
34991      * @param {Number} col The column index
34992      * @param {Function} fn The function to use to process the cell's raw data
34993      * to return HTML markup for the grid view. The render function is called with
34994      * the following parameters:<ul>
34995      * <li>Data value.</li>
34996      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34997      * <li>css A CSS style string to apply to the table cell.</li>
34998      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34999      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35000      * <li>Row index</li>
35001      * <li>Column index</li>
35002      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35003      */
35004     setRenderer : function(col, fn){
35005         this.config[col].renderer = fn;
35006     },
35007
35008     /**
35009      * Returns the width for the specified column.
35010      * @param {Number} col The column index
35011      * @return {Number}
35012      */
35013     getColumnWidth : function(col){
35014         return this.config[col].width * 1 || this.defaultWidth;
35015     },
35016
35017     /**
35018      * Sets the width for a column.
35019      * @param {Number} col The column index
35020      * @param {Number} width The new width
35021      */
35022     setColumnWidth : function(col, width, suppressEvent){
35023         this.config[col].width = width;
35024         this.totalWidth = null;
35025         if(!suppressEvent){
35026              this.fireEvent("widthchange", this, col, width);
35027         }
35028     },
35029
35030     /**
35031      * Returns the total width of all columns.
35032      * @param {Boolean} includeHidden True to include hidden column widths
35033      * @return {Number}
35034      */
35035     getTotalWidth : function(includeHidden){
35036         if(!this.totalWidth){
35037             this.totalWidth = 0;
35038             for(var i = 0, len = this.config.length; i < len; i++){
35039                 if(includeHidden || !this.isHidden(i)){
35040                     this.totalWidth += this.getColumnWidth(i);
35041                 }
35042             }
35043         }
35044         return this.totalWidth;
35045     },
35046
35047     /**
35048      * Returns the header for the specified column.
35049      * @param {Number} col The column index
35050      * @return {String}
35051      */
35052     getColumnHeader : function(col){
35053         return this.config[col].header;
35054     },
35055
35056     /**
35057      * Sets the header for a column.
35058      * @param {Number} col The column index
35059      * @param {String} header The new header
35060      */
35061     setColumnHeader : function(col, header){
35062         this.config[col].header = header;
35063         this.fireEvent("headerchange", this, col, header);
35064     },
35065
35066     /**
35067      * Returns the tooltip for the specified column.
35068      * @param {Number} col The column index
35069      * @return {String}
35070      */
35071     getColumnTooltip : function(col){
35072             return this.config[col].tooltip;
35073     },
35074     /**
35075      * Sets the tooltip for a column.
35076      * @param {Number} col The column index
35077      * @param {String} tooltip The new tooltip
35078      */
35079     setColumnTooltip : function(col, tooltip){
35080             this.config[col].tooltip = tooltip;
35081     },
35082
35083     /**
35084      * Returns the dataIndex for the specified column.
35085      * @param {Number} col The column index
35086      * @return {Number}
35087      */
35088     getDataIndex : function(col){
35089         return this.config[col].dataIndex;
35090     },
35091
35092     /**
35093      * Sets the dataIndex for a column.
35094      * @param {Number} col The column index
35095      * @param {Number} dataIndex The new dataIndex
35096      */
35097     setDataIndex : function(col, dataIndex){
35098         this.config[col].dataIndex = dataIndex;
35099     },
35100
35101     
35102     
35103     /**
35104      * Returns true if the cell is editable.
35105      * @param {Number} colIndex The column index
35106      * @param {Number} rowIndex The row index - this is nto actually used..?
35107      * @return {Boolean}
35108      */
35109     isCellEditable : function(colIndex, rowIndex){
35110         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35111     },
35112
35113     /**
35114      * Returns the editor defined for the cell/column.
35115      * return false or null to disable editing.
35116      * @param {Number} colIndex The column index
35117      * @param {Number} rowIndex The row index
35118      * @return {Object}
35119      */
35120     getCellEditor : function(colIndex, rowIndex){
35121         return this.config[colIndex].editor;
35122     },
35123
35124     /**
35125      * Sets if a column is editable.
35126      * @param {Number} col The column index
35127      * @param {Boolean} editable True if the column is editable
35128      */
35129     setEditable : function(col, editable){
35130         this.config[col].editable = editable;
35131     },
35132
35133
35134     /**
35135      * Returns true if the column is hidden.
35136      * @param {Number} colIndex The column index
35137      * @return {Boolean}
35138      */
35139     isHidden : function(colIndex){
35140         return this.config[colIndex].hidden;
35141     },
35142
35143
35144     /**
35145      * Returns true if the column width cannot be changed
35146      */
35147     isFixed : function(colIndex){
35148         return this.config[colIndex].fixed;
35149     },
35150
35151     /**
35152      * Returns true if the column can be resized
35153      * @return {Boolean}
35154      */
35155     isResizable : function(colIndex){
35156         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35157     },
35158     /**
35159      * Sets if a column is hidden.
35160      * @param {Number} colIndex The column index
35161      * @param {Boolean} hidden True if the column is hidden
35162      */
35163     setHidden : function(colIndex, hidden){
35164         this.config[colIndex].hidden = hidden;
35165         this.totalWidth = null;
35166         this.fireEvent("hiddenchange", this, colIndex, hidden);
35167     },
35168
35169     /**
35170      * Sets the editor for a column.
35171      * @param {Number} col The column index
35172      * @param {Object} editor The editor object
35173      */
35174     setEditor : function(col, editor){
35175         this.config[col].editor = editor;
35176     }
35177 });
35178
35179 Roo.grid.ColumnModel.defaultRenderer = function(value)
35180 {
35181     if(typeof value == "object") {
35182         return value;
35183     }
35184         if(typeof value == "string" && value.length < 1){
35185             return "&#160;";
35186         }
35187     
35188         return String.format("{0}", value);
35189 };
35190
35191 // Alias for backwards compatibility
35192 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35193 /*
35194  * Based on:
35195  * Ext JS Library 1.1.1
35196  * Copyright(c) 2006-2007, Ext JS, LLC.
35197  *
35198  * Originally Released Under LGPL - original licence link has changed is not relivant.
35199  *
35200  * Fork - LGPL
35201  * <script type="text/javascript">
35202  */
35203
35204 /**
35205  * @class Roo.grid.AbstractSelectionModel
35206  * @extends Roo.util.Observable
35207  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35208  * implemented by descendant classes.  This class should not be directly instantiated.
35209  * @constructor
35210  */
35211 Roo.grid.AbstractSelectionModel = function(){
35212     this.locked = false;
35213     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35214 };
35215
35216 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35217     /** @ignore Called by the grid automatically. Do not call directly. */
35218     init : function(grid){
35219         this.grid = grid;
35220         this.initEvents();
35221     },
35222
35223     /**
35224      * Locks the selections.
35225      */
35226     lock : function(){
35227         this.locked = true;
35228     },
35229
35230     /**
35231      * Unlocks the selections.
35232      */
35233     unlock : function(){
35234         this.locked = false;
35235     },
35236
35237     /**
35238      * Returns true if the selections are locked.
35239      * @return {Boolean}
35240      */
35241     isLocked : function(){
35242         return this.locked;
35243     }
35244 });/*
35245  * Based on:
35246  * Ext JS Library 1.1.1
35247  * Copyright(c) 2006-2007, Ext JS, LLC.
35248  *
35249  * Originally Released Under LGPL - original licence link has changed is not relivant.
35250  *
35251  * Fork - LGPL
35252  * <script type="text/javascript">
35253  */
35254 /**
35255  * @extends Roo.grid.AbstractSelectionModel
35256  * @class Roo.grid.RowSelectionModel
35257  * The default SelectionModel used by {@link Roo.grid.Grid}.
35258  * It supports multiple selections and keyboard selection/navigation. 
35259  * @constructor
35260  * @param {Object} config
35261  */
35262 Roo.grid.RowSelectionModel = function(config){
35263     Roo.apply(this, config);
35264     this.selections = new Roo.util.MixedCollection(false, function(o){
35265         return o.id;
35266     });
35267
35268     this.last = false;
35269     this.lastActive = false;
35270
35271     this.addEvents({
35272         /**
35273              * @event selectionchange
35274              * Fires when the selection changes
35275              * @param {SelectionModel} this
35276              */
35277             "selectionchange" : true,
35278         /**
35279              * @event afterselectionchange
35280              * Fires after the selection changes (eg. by key press or clicking)
35281              * @param {SelectionModel} this
35282              */
35283             "afterselectionchange" : true,
35284         /**
35285              * @event beforerowselect
35286              * Fires when a row is selected being selected, return false to cancel.
35287              * @param {SelectionModel} this
35288              * @param {Number} rowIndex The selected index
35289              * @param {Boolean} keepExisting False if other selections will be cleared
35290              */
35291             "beforerowselect" : true,
35292         /**
35293              * @event rowselect
35294              * Fires when a row is selected.
35295              * @param {SelectionModel} this
35296              * @param {Number} rowIndex The selected index
35297              * @param {Roo.data.Record} r The record
35298              */
35299             "rowselect" : true,
35300         /**
35301              * @event rowdeselect
35302              * Fires when a row is deselected.
35303              * @param {SelectionModel} this
35304              * @param {Number} rowIndex The selected index
35305              */
35306         "rowdeselect" : true
35307     });
35308     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35309     this.locked = false;
35310 };
35311
35312 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35313     /**
35314      * @cfg {Boolean} singleSelect
35315      * True to allow selection of only one row at a time (defaults to false)
35316      */
35317     singleSelect : false,
35318
35319     // private
35320     initEvents : function(){
35321
35322         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35323             this.grid.on("mousedown", this.handleMouseDown, this);
35324         }else{ // allow click to work like normal
35325             this.grid.on("rowclick", this.handleDragableRowClick, this);
35326         }
35327
35328         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35329             "up" : function(e){
35330                 if(!e.shiftKey){
35331                     this.selectPrevious(e.shiftKey);
35332                 }else if(this.last !== false && this.lastActive !== false){
35333                     var last = this.last;
35334                     this.selectRange(this.last,  this.lastActive-1);
35335                     this.grid.getView().focusRow(this.lastActive);
35336                     if(last !== false){
35337                         this.last = last;
35338                     }
35339                 }else{
35340                     this.selectFirstRow();
35341                 }
35342                 this.fireEvent("afterselectionchange", this);
35343             },
35344             "down" : function(e){
35345                 if(!e.shiftKey){
35346                     this.selectNext(e.shiftKey);
35347                 }else if(this.last !== false && this.lastActive !== false){
35348                     var last = this.last;
35349                     this.selectRange(this.last,  this.lastActive+1);
35350                     this.grid.getView().focusRow(this.lastActive);
35351                     if(last !== false){
35352                         this.last = last;
35353                     }
35354                 }else{
35355                     this.selectFirstRow();
35356                 }
35357                 this.fireEvent("afterselectionchange", this);
35358             },
35359             scope: this
35360         });
35361
35362         var view = this.grid.view;
35363         view.on("refresh", this.onRefresh, this);
35364         view.on("rowupdated", this.onRowUpdated, this);
35365         view.on("rowremoved", this.onRemove, this);
35366     },
35367
35368     // private
35369     onRefresh : function(){
35370         var ds = this.grid.dataSource, i, v = this.grid.view;
35371         var s = this.selections;
35372         s.each(function(r){
35373             if((i = ds.indexOfId(r.id)) != -1){
35374                 v.onRowSelect(i);
35375                 s.add(ds.getAt(i)); // updating the selection relate data
35376             }else{
35377                 s.remove(r);
35378             }
35379         });
35380     },
35381
35382     // private
35383     onRemove : function(v, index, r){
35384         this.selections.remove(r);
35385     },
35386
35387     // private
35388     onRowUpdated : function(v, index, r){
35389         if(this.isSelected(r)){
35390             v.onRowSelect(index);
35391         }
35392     },
35393
35394     /**
35395      * Select records.
35396      * @param {Array} records The records to select
35397      * @param {Boolean} keepExisting (optional) True to keep existing selections
35398      */
35399     selectRecords : function(records, keepExisting){
35400         if(!keepExisting){
35401             this.clearSelections();
35402         }
35403         var ds = this.grid.dataSource;
35404         for(var i = 0, len = records.length; i < len; i++){
35405             this.selectRow(ds.indexOf(records[i]), true);
35406         }
35407     },
35408
35409     /**
35410      * Gets the number of selected rows.
35411      * @return {Number}
35412      */
35413     getCount : function(){
35414         return this.selections.length;
35415     },
35416
35417     /**
35418      * Selects the first row in the grid.
35419      */
35420     selectFirstRow : function(){
35421         this.selectRow(0);
35422     },
35423
35424     /**
35425      * Select the last row.
35426      * @param {Boolean} keepExisting (optional) True to keep existing selections
35427      */
35428     selectLastRow : function(keepExisting){
35429         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35430     },
35431
35432     /**
35433      * Selects the row immediately following the last selected row.
35434      * @param {Boolean} keepExisting (optional) True to keep existing selections
35435      */
35436     selectNext : function(keepExisting){
35437         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35438             this.selectRow(this.last+1, keepExisting);
35439             this.grid.getView().focusRow(this.last);
35440         }
35441     },
35442
35443     /**
35444      * Selects the row that precedes the last selected row.
35445      * @param {Boolean} keepExisting (optional) True to keep existing selections
35446      */
35447     selectPrevious : function(keepExisting){
35448         if(this.last){
35449             this.selectRow(this.last-1, keepExisting);
35450             this.grid.getView().focusRow(this.last);
35451         }
35452     },
35453
35454     /**
35455      * Returns the selected records
35456      * @return {Array} Array of selected records
35457      */
35458     getSelections : function(){
35459         return [].concat(this.selections.items);
35460     },
35461
35462     /**
35463      * Returns the first selected record.
35464      * @return {Record}
35465      */
35466     getSelected : function(){
35467         return this.selections.itemAt(0);
35468     },
35469
35470
35471     /**
35472      * Clears all selections.
35473      */
35474     clearSelections : function(fast){
35475         if(this.locked) {
35476             return;
35477         }
35478         if(fast !== true){
35479             var ds = this.grid.dataSource;
35480             var s = this.selections;
35481             s.each(function(r){
35482                 this.deselectRow(ds.indexOfId(r.id));
35483             }, this);
35484             s.clear();
35485         }else{
35486             this.selections.clear();
35487         }
35488         this.last = false;
35489     },
35490
35491
35492     /**
35493      * Selects all rows.
35494      */
35495     selectAll : function(){
35496         if(this.locked) {
35497             return;
35498         }
35499         this.selections.clear();
35500         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35501             this.selectRow(i, true);
35502         }
35503     },
35504
35505     /**
35506      * Returns True if there is a selection.
35507      * @return {Boolean}
35508      */
35509     hasSelection : function(){
35510         return this.selections.length > 0;
35511     },
35512
35513     /**
35514      * Returns True if the specified row is selected.
35515      * @param {Number/Record} record The record or index of the record to check
35516      * @return {Boolean}
35517      */
35518     isSelected : function(index){
35519         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35520         return (r && this.selections.key(r.id) ? true : false);
35521     },
35522
35523     /**
35524      * Returns True if the specified record id is selected.
35525      * @param {String} id The id of record to check
35526      * @return {Boolean}
35527      */
35528     isIdSelected : function(id){
35529         return (this.selections.key(id) ? true : false);
35530     },
35531
35532     // private
35533     handleMouseDown : function(e, t){
35534         var view = this.grid.getView(), rowIndex;
35535         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35536             return;
35537         };
35538         if(e.shiftKey && this.last !== false){
35539             var last = this.last;
35540             this.selectRange(last, rowIndex, e.ctrlKey);
35541             this.last = last; // reset the last
35542             view.focusRow(rowIndex);
35543         }else{
35544             var isSelected = this.isSelected(rowIndex);
35545             if(e.button !== 0 && isSelected){
35546                 view.focusRow(rowIndex);
35547             }else if(e.ctrlKey && isSelected){
35548                 this.deselectRow(rowIndex);
35549             }else if(!isSelected){
35550                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35551                 view.focusRow(rowIndex);
35552             }
35553         }
35554         this.fireEvent("afterselectionchange", this);
35555     },
35556     // private
35557     handleDragableRowClick :  function(grid, rowIndex, e) 
35558     {
35559         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35560             this.selectRow(rowIndex, false);
35561             grid.view.focusRow(rowIndex);
35562              this.fireEvent("afterselectionchange", this);
35563         }
35564     },
35565     
35566     /**
35567      * Selects multiple rows.
35568      * @param {Array} rows Array of the indexes of the row to select
35569      * @param {Boolean} keepExisting (optional) True to keep existing selections
35570      */
35571     selectRows : function(rows, keepExisting){
35572         if(!keepExisting){
35573             this.clearSelections();
35574         }
35575         for(var i = 0, len = rows.length; i < len; i++){
35576             this.selectRow(rows[i], true);
35577         }
35578     },
35579
35580     /**
35581      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35582      * @param {Number} startRow The index of the first row in the range
35583      * @param {Number} endRow The index of the last row in the range
35584      * @param {Boolean} keepExisting (optional) True to retain existing selections
35585      */
35586     selectRange : function(startRow, endRow, keepExisting){
35587         if(this.locked) {
35588             return;
35589         }
35590         if(!keepExisting){
35591             this.clearSelections();
35592         }
35593         if(startRow <= endRow){
35594             for(var i = startRow; i <= endRow; i++){
35595                 this.selectRow(i, true);
35596             }
35597         }else{
35598             for(var i = startRow; i >= endRow; i--){
35599                 this.selectRow(i, true);
35600             }
35601         }
35602     },
35603
35604     /**
35605      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35606      * @param {Number} startRow The index of the first row in the range
35607      * @param {Number} endRow The index of the last row in the range
35608      */
35609     deselectRange : function(startRow, endRow, preventViewNotify){
35610         if(this.locked) {
35611             return;
35612         }
35613         for(var i = startRow; i <= endRow; i++){
35614             this.deselectRow(i, preventViewNotify);
35615         }
35616     },
35617
35618     /**
35619      * Selects a row.
35620      * @param {Number} row The index of the row to select
35621      * @param {Boolean} keepExisting (optional) True to keep existing selections
35622      */
35623     selectRow : function(index, keepExisting, preventViewNotify){
35624         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35625             return;
35626         }
35627         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35628             if(!keepExisting || this.singleSelect){
35629                 this.clearSelections();
35630             }
35631             var r = this.grid.dataSource.getAt(index);
35632             this.selections.add(r);
35633             this.last = this.lastActive = index;
35634             if(!preventViewNotify){
35635                 this.grid.getView().onRowSelect(index);
35636             }
35637             this.fireEvent("rowselect", this, index, r);
35638             this.fireEvent("selectionchange", this);
35639         }
35640     },
35641
35642     /**
35643      * Deselects a row.
35644      * @param {Number} row The index of the row to deselect
35645      */
35646     deselectRow : function(index, preventViewNotify){
35647         if(this.locked) {
35648             return;
35649         }
35650         if(this.last == index){
35651             this.last = false;
35652         }
35653         if(this.lastActive == index){
35654             this.lastActive = false;
35655         }
35656         var r = this.grid.dataSource.getAt(index);
35657         this.selections.remove(r);
35658         if(!preventViewNotify){
35659             this.grid.getView().onRowDeselect(index);
35660         }
35661         this.fireEvent("rowdeselect", this, index);
35662         this.fireEvent("selectionchange", this);
35663     },
35664
35665     // private
35666     restoreLast : function(){
35667         if(this._last){
35668             this.last = this._last;
35669         }
35670     },
35671
35672     // private
35673     acceptsNav : function(row, col, cm){
35674         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35675     },
35676
35677     // private
35678     onEditorKey : function(field, e){
35679         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35680         if(k == e.TAB){
35681             e.stopEvent();
35682             ed.completeEdit();
35683             if(e.shiftKey){
35684                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35685             }else{
35686                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35687             }
35688         }else if(k == e.ENTER && !e.ctrlKey){
35689             e.stopEvent();
35690             ed.completeEdit();
35691             if(e.shiftKey){
35692                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35693             }else{
35694                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35695             }
35696         }else if(k == e.ESC){
35697             ed.cancelEdit();
35698         }
35699         if(newCell){
35700             g.startEditing(newCell[0], newCell[1]);
35701         }
35702     }
35703 });/*
35704  * Based on:
35705  * Ext JS Library 1.1.1
35706  * Copyright(c) 2006-2007, Ext JS, LLC.
35707  *
35708  * Originally Released Under LGPL - original licence link has changed is not relivant.
35709  *
35710  * Fork - LGPL
35711  * <script type="text/javascript">
35712  */
35713 /**
35714  * @class Roo.grid.CellSelectionModel
35715  * @extends Roo.grid.AbstractSelectionModel
35716  * This class provides the basic implementation for cell selection in a grid.
35717  * @constructor
35718  * @param {Object} config The object containing the configuration of this model.
35719  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35720  */
35721 Roo.grid.CellSelectionModel = function(config){
35722     Roo.apply(this, config);
35723
35724     this.selection = null;
35725
35726     this.addEvents({
35727         /**
35728              * @event beforerowselect
35729              * Fires before a cell is selected.
35730              * @param {SelectionModel} this
35731              * @param {Number} rowIndex The selected row index
35732              * @param {Number} colIndex The selected cell index
35733              */
35734             "beforecellselect" : true,
35735         /**
35736              * @event cellselect
35737              * Fires when a cell is selected.
35738              * @param {SelectionModel} this
35739              * @param {Number} rowIndex The selected row index
35740              * @param {Number} colIndex The selected cell index
35741              */
35742             "cellselect" : true,
35743         /**
35744              * @event selectionchange
35745              * Fires when the active selection changes.
35746              * @param {SelectionModel} this
35747              * @param {Object} selection null for no selection or an object (o) with two properties
35748                 <ul>
35749                 <li>o.record: the record object for the row the selection is in</li>
35750                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35751                 </ul>
35752              */
35753             "selectionchange" : true,
35754         /**
35755              * @event tabend
35756              * Fires when the tab (or enter) was pressed on the last editable cell
35757              * You can use this to trigger add new row.
35758              * @param {SelectionModel} this
35759              */
35760             "tabend" : true,
35761          /**
35762              * @event beforeeditnext
35763              * Fires before the next editable sell is made active
35764              * You can use this to skip to another cell or fire the tabend
35765              *    if you set cell to false
35766              * @param {Object} eventdata object : { cell : [ row, col ] } 
35767              */
35768             "beforeeditnext" : true
35769     });
35770     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35771 };
35772
35773 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35774     
35775     enter_is_tab: false,
35776
35777     /** @ignore */
35778     initEvents : function(){
35779         this.grid.on("mousedown", this.handleMouseDown, this);
35780         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35781         var view = this.grid.view;
35782         view.on("refresh", this.onViewChange, this);
35783         view.on("rowupdated", this.onRowUpdated, this);
35784         view.on("beforerowremoved", this.clearSelections, this);
35785         view.on("beforerowsinserted", this.clearSelections, this);
35786         if(this.grid.isEditor){
35787             this.grid.on("beforeedit", this.beforeEdit,  this);
35788         }
35789     },
35790
35791         //private
35792     beforeEdit : function(e){
35793         this.select(e.row, e.column, false, true, e.record);
35794     },
35795
35796         //private
35797     onRowUpdated : function(v, index, r){
35798         if(this.selection && this.selection.record == r){
35799             v.onCellSelect(index, this.selection.cell[1]);
35800         }
35801     },
35802
35803         //private
35804     onViewChange : function(){
35805         this.clearSelections(true);
35806     },
35807
35808         /**
35809          * Returns the currently selected cell,.
35810          * @return {Array} The selected cell (row, column) or null if none selected.
35811          */
35812     getSelectedCell : function(){
35813         return this.selection ? this.selection.cell : null;
35814     },
35815
35816     /**
35817      * Clears all selections.
35818      * @param {Boolean} true to prevent the gridview from being notified about the change.
35819      */
35820     clearSelections : function(preventNotify){
35821         var s = this.selection;
35822         if(s){
35823             if(preventNotify !== true){
35824                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35825             }
35826             this.selection = null;
35827             this.fireEvent("selectionchange", this, null);
35828         }
35829     },
35830
35831     /**
35832      * Returns true if there is a selection.
35833      * @return {Boolean}
35834      */
35835     hasSelection : function(){
35836         return this.selection ? true : false;
35837     },
35838
35839     /** @ignore */
35840     handleMouseDown : function(e, t){
35841         var v = this.grid.getView();
35842         if(this.isLocked()){
35843             return;
35844         };
35845         var row = v.findRowIndex(t);
35846         var cell = v.findCellIndex(t);
35847         if(row !== false && cell !== false){
35848             this.select(row, cell);
35849         }
35850     },
35851
35852     /**
35853      * Selects a cell.
35854      * @param {Number} rowIndex
35855      * @param {Number} collIndex
35856      */
35857     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35858         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35859             this.clearSelections();
35860             r = r || this.grid.dataSource.getAt(rowIndex);
35861             this.selection = {
35862                 record : r,
35863                 cell : [rowIndex, colIndex]
35864             };
35865             if(!preventViewNotify){
35866                 var v = this.grid.getView();
35867                 v.onCellSelect(rowIndex, colIndex);
35868                 if(preventFocus !== true){
35869                     v.focusCell(rowIndex, colIndex);
35870                 }
35871             }
35872             this.fireEvent("cellselect", this, rowIndex, colIndex);
35873             this.fireEvent("selectionchange", this, this.selection);
35874         }
35875     },
35876
35877         //private
35878     isSelectable : function(rowIndex, colIndex, cm){
35879         return !cm.isHidden(colIndex);
35880     },
35881
35882     /** @ignore */
35883     handleKeyDown : function(e){
35884         //Roo.log('Cell Sel Model handleKeyDown');
35885         if(!e.isNavKeyPress()){
35886             return;
35887         }
35888         var g = this.grid, s = this.selection;
35889         if(!s){
35890             e.stopEvent();
35891             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35892             if(cell){
35893                 this.select(cell[0], cell[1]);
35894             }
35895             return;
35896         }
35897         var sm = this;
35898         var walk = function(row, col, step){
35899             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35900         };
35901         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35902         var newCell;
35903
35904       
35905
35906         switch(k){
35907             case e.TAB:
35908                 // handled by onEditorKey
35909                 if (g.isEditor && g.editing) {
35910                     return;
35911                 }
35912                 if(e.shiftKey) {
35913                     newCell = walk(r, c-1, -1);
35914                 } else {
35915                     newCell = walk(r, c+1, 1);
35916                 }
35917                 break;
35918             
35919             case e.DOWN:
35920                newCell = walk(r+1, c, 1);
35921                 break;
35922             
35923             case e.UP:
35924                 newCell = walk(r-1, c, -1);
35925                 break;
35926             
35927             case e.RIGHT:
35928                 newCell = walk(r, c+1, 1);
35929                 break;
35930             
35931             case e.LEFT:
35932                 newCell = walk(r, c-1, -1);
35933                 break;
35934             
35935             case e.ENTER:
35936                 
35937                 if(g.isEditor && !g.editing){
35938                    g.startEditing(r, c);
35939                    e.stopEvent();
35940                    return;
35941                 }
35942                 
35943                 
35944              break;
35945         };
35946         if(newCell){
35947             this.select(newCell[0], newCell[1]);
35948             e.stopEvent();
35949             
35950         }
35951     },
35952
35953     acceptsNav : function(row, col, cm){
35954         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35955     },
35956     /**
35957      * Selects a cell.
35958      * @param {Number} field (not used) - as it's normally used as a listener
35959      * @param {Number} e - event - fake it by using
35960      *
35961      * var e = Roo.EventObjectImpl.prototype;
35962      * e.keyCode = e.TAB
35963      *
35964      * 
35965      */
35966     onEditorKey : function(field, e){
35967         
35968         var k = e.getKey(),
35969             newCell,
35970             g = this.grid,
35971             ed = g.activeEditor,
35972             forward = false;
35973         ///Roo.log('onEditorKey' + k);
35974         
35975         
35976         if (this.enter_is_tab && k == e.ENTER) {
35977             k = e.TAB;
35978         }
35979         
35980         if(k == e.TAB){
35981             if(e.shiftKey){
35982                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35983             }else{
35984                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35985                 forward = true;
35986             }
35987             
35988             e.stopEvent();
35989             
35990         } else if(k == e.ENTER &&  !e.ctrlKey){
35991             ed.completeEdit();
35992             e.stopEvent();
35993             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35994         
35995                 } else if(k == e.ESC){
35996             ed.cancelEdit();
35997         }
35998                 
35999         if (newCell) {
36000             var ecall = { cell : newCell, forward : forward };
36001             this.fireEvent('beforeeditnext', ecall );
36002             newCell = ecall.cell;
36003                         forward = ecall.forward;
36004         }
36005                 
36006         if(newCell){
36007             //Roo.log('next cell after edit');
36008             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36009         } else if (forward) {
36010             // tabbed past last
36011             this.fireEvent.defer(100, this, ['tabend',this]);
36012         }
36013     }
36014 });/*
36015  * Based on:
36016  * Ext JS Library 1.1.1
36017  * Copyright(c) 2006-2007, Ext JS, LLC.
36018  *
36019  * Originally Released Under LGPL - original licence link has changed is not relivant.
36020  *
36021  * Fork - LGPL
36022  * <script type="text/javascript">
36023  */
36024  
36025 /**
36026  * @class Roo.grid.EditorGrid
36027  * @extends Roo.grid.Grid
36028  * Class for creating and editable grid.
36029  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36030  * The container MUST have some type of size defined for the grid to fill. The container will be 
36031  * automatically set to position relative if it isn't already.
36032  * @param {Object} dataSource The data model to bind to
36033  * @param {Object} colModel The column model with info about this grid's columns
36034  */
36035 Roo.grid.EditorGrid = function(container, config){
36036     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36037     this.getGridEl().addClass("xedit-grid");
36038
36039     if(!this.selModel){
36040         this.selModel = new Roo.grid.CellSelectionModel();
36041     }
36042
36043     this.activeEditor = null;
36044
36045         this.addEvents({
36046             /**
36047              * @event beforeedit
36048              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36049              * <ul style="padding:5px;padding-left:16px;">
36050              * <li>grid - This grid</li>
36051              * <li>record - The record being edited</li>
36052              * <li>field - The field name being edited</li>
36053              * <li>value - The value for the field being edited.</li>
36054              * <li>row - The grid row index</li>
36055              * <li>column - The grid column index</li>
36056              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36057              * </ul>
36058              * @param {Object} e An edit event (see above for description)
36059              */
36060             "beforeedit" : true,
36061             /**
36062              * @event afteredit
36063              * Fires after a cell is edited. <br />
36064              * <ul style="padding:5px;padding-left:16px;">
36065              * <li>grid - This grid</li>
36066              * <li>record - The record being edited</li>
36067              * <li>field - The field name being edited</li>
36068              * <li>value - The value being set</li>
36069              * <li>originalValue - The original value for the field, before the edit.</li>
36070              * <li>row - The grid row index</li>
36071              * <li>column - The grid column index</li>
36072              * </ul>
36073              * @param {Object} e An edit event (see above for description)
36074              */
36075             "afteredit" : true,
36076             /**
36077              * @event validateedit
36078              * Fires after a cell is edited, but before the value is set in the record. 
36079          * You can use this to modify the value being set in the field, Return false
36080              * to cancel the change. The edit event object has the following properties <br />
36081              * <ul style="padding:5px;padding-left:16px;">
36082          * <li>editor - This editor</li>
36083              * <li>grid - This grid</li>
36084              * <li>record - The record being edited</li>
36085              * <li>field - The field name being edited</li>
36086              * <li>value - The value being set</li>
36087              * <li>originalValue - The original value for the field, before the edit.</li>
36088              * <li>row - The grid row index</li>
36089              * <li>column - The grid column index</li>
36090              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36091              * </ul>
36092              * @param {Object} e An edit event (see above for description)
36093              */
36094             "validateedit" : true
36095         });
36096     this.on("bodyscroll", this.stopEditing,  this);
36097     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36098 };
36099
36100 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36101     /**
36102      * @cfg {Number} clicksToEdit
36103      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36104      */
36105     clicksToEdit: 2,
36106
36107     // private
36108     isEditor : true,
36109     // private
36110     trackMouseOver: false, // causes very odd FF errors
36111
36112     onCellDblClick : function(g, row, col){
36113         this.startEditing(row, col);
36114     },
36115
36116     onEditComplete : function(ed, value, startValue){
36117         this.editing = false;
36118         this.activeEditor = null;
36119         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36120         var r = ed.record;
36121         var field = this.colModel.getDataIndex(ed.col);
36122         var e = {
36123             grid: this,
36124             record: r,
36125             field: field,
36126             originalValue: startValue,
36127             value: value,
36128             row: ed.row,
36129             column: ed.col,
36130             cancel:false,
36131             editor: ed
36132         };
36133         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36134         cell.show();
36135           
36136         if(String(value) !== String(startValue)){
36137             
36138             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36139                 r.set(field, e.value);
36140                 // if we are dealing with a combo box..
36141                 // then we also set the 'name' colum to be the displayField
36142                 if (ed.field.displayField && ed.field.name) {
36143                     r.set(ed.field.name, ed.field.el.dom.value);
36144                 }
36145                 
36146                 delete e.cancel; //?? why!!!
36147                 this.fireEvent("afteredit", e);
36148             }
36149         } else {
36150             this.fireEvent("afteredit", e); // always fire it!
36151         }
36152         this.view.focusCell(ed.row, ed.col);
36153     },
36154
36155     /**
36156      * Starts editing the specified for the specified row/column
36157      * @param {Number} rowIndex
36158      * @param {Number} colIndex
36159      */
36160     startEditing : function(row, col){
36161         this.stopEditing();
36162         if(this.colModel.isCellEditable(col, row)){
36163             this.view.ensureVisible(row, col, true);
36164           
36165             var r = this.dataSource.getAt(row);
36166             var field = this.colModel.getDataIndex(col);
36167             var cell = Roo.get(this.view.getCell(row,col));
36168             var e = {
36169                 grid: this,
36170                 record: r,
36171                 field: field,
36172                 value: r.data[field],
36173                 row: row,
36174                 column: col,
36175                 cancel:false 
36176             };
36177             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36178                 this.editing = true;
36179                 var ed = this.colModel.getCellEditor(col, row);
36180                 
36181                 if (!ed) {
36182                     return;
36183                 }
36184                 if(!ed.rendered){
36185                     ed.render(ed.parentEl || document.body);
36186                 }
36187                 ed.field.reset();
36188                
36189                 cell.hide();
36190                 
36191                 (function(){ // complex but required for focus issues in safari, ie and opera
36192                     ed.row = row;
36193                     ed.col = col;
36194                     ed.record = r;
36195                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36196                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36197                     this.activeEditor = ed;
36198                     var v = r.data[field];
36199                     ed.startEdit(this.view.getCell(row, col), v);
36200                     // combo's with 'displayField and name set
36201                     if (ed.field.displayField && ed.field.name) {
36202                         ed.field.el.dom.value = r.data[ed.field.name];
36203                     }
36204                     
36205                     
36206                 }).defer(50, this);
36207             }
36208         }
36209     },
36210         
36211     /**
36212      * Stops any active editing
36213      */
36214     stopEditing : function(){
36215         if(this.activeEditor){
36216             this.activeEditor.completeEdit();
36217         }
36218         this.activeEditor = null;
36219     },
36220         
36221          /**
36222      * Called to get grid's drag proxy text, by default returns this.ddText.
36223      * @return {String}
36224      */
36225     getDragDropText : function(){
36226         var count = this.selModel.getSelectedCell() ? 1 : 0;
36227         return String.format(this.ddText, count, count == 1 ? '' : 's');
36228     }
36229         
36230 });/*
36231  * Based on:
36232  * Ext JS Library 1.1.1
36233  * Copyright(c) 2006-2007, Ext JS, LLC.
36234  *
36235  * Originally Released Under LGPL - original licence link has changed is not relivant.
36236  *
36237  * Fork - LGPL
36238  * <script type="text/javascript">
36239  */
36240
36241 // private - not really -- you end up using it !
36242 // This is a support class used internally by the Grid components
36243
36244 /**
36245  * @class Roo.grid.GridEditor
36246  * @extends Roo.Editor
36247  * Class for creating and editable grid elements.
36248  * @param {Object} config any settings (must include field)
36249  */
36250 Roo.grid.GridEditor = function(field, config){
36251     if (!config && field.field) {
36252         config = field;
36253         field = Roo.factory(config.field, Roo.form);
36254     }
36255     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36256     field.monitorTab = false;
36257 };
36258
36259 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36260     
36261     /**
36262      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36263      */
36264     
36265     alignment: "tl-tl",
36266     autoSize: "width",
36267     hideEl : false,
36268     cls: "x-small-editor x-grid-editor",
36269     shim:false,
36270     shadow:"frame"
36271 });/*
36272  * Based on:
36273  * Ext JS Library 1.1.1
36274  * Copyright(c) 2006-2007, Ext JS, LLC.
36275  *
36276  * Originally Released Under LGPL - original licence link has changed is not relivant.
36277  *
36278  * Fork - LGPL
36279  * <script type="text/javascript">
36280  */
36281   
36282
36283   
36284 Roo.grid.PropertyRecord = Roo.data.Record.create([
36285     {name:'name',type:'string'},  'value'
36286 ]);
36287
36288
36289 Roo.grid.PropertyStore = function(grid, source){
36290     this.grid = grid;
36291     this.store = new Roo.data.Store({
36292         recordType : Roo.grid.PropertyRecord
36293     });
36294     this.store.on('update', this.onUpdate,  this);
36295     if(source){
36296         this.setSource(source);
36297     }
36298     Roo.grid.PropertyStore.superclass.constructor.call(this);
36299 };
36300
36301
36302
36303 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36304     setSource : function(o){
36305         this.source = o;
36306         this.store.removeAll();
36307         var data = [];
36308         for(var k in o){
36309             if(this.isEditableValue(o[k])){
36310                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36311             }
36312         }
36313         this.store.loadRecords({records: data}, {}, true);
36314     },
36315
36316     onUpdate : function(ds, record, type){
36317         if(type == Roo.data.Record.EDIT){
36318             var v = record.data['value'];
36319             var oldValue = record.modified['value'];
36320             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36321                 this.source[record.id] = v;
36322                 record.commit();
36323                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36324             }else{
36325                 record.reject();
36326             }
36327         }
36328     },
36329
36330     getProperty : function(row){
36331        return this.store.getAt(row);
36332     },
36333
36334     isEditableValue: function(val){
36335         if(val && val instanceof Date){
36336             return true;
36337         }else if(typeof val == 'object' || typeof val == 'function'){
36338             return false;
36339         }
36340         return true;
36341     },
36342
36343     setValue : function(prop, value){
36344         this.source[prop] = value;
36345         this.store.getById(prop).set('value', value);
36346     },
36347
36348     getSource : function(){
36349         return this.source;
36350     }
36351 });
36352
36353 Roo.grid.PropertyColumnModel = function(grid, store){
36354     this.grid = grid;
36355     var g = Roo.grid;
36356     g.PropertyColumnModel.superclass.constructor.call(this, [
36357         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36358         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36359     ]);
36360     this.store = store;
36361     this.bselect = Roo.DomHelper.append(document.body, {
36362         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36363             {tag: 'option', value: 'true', html: 'true'},
36364             {tag: 'option', value: 'false', html: 'false'}
36365         ]
36366     });
36367     Roo.id(this.bselect);
36368     var f = Roo.form;
36369     this.editors = {
36370         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36371         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36372         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36373         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36374         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36375     };
36376     this.renderCellDelegate = this.renderCell.createDelegate(this);
36377     this.renderPropDelegate = this.renderProp.createDelegate(this);
36378 };
36379
36380 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36381     
36382     
36383     nameText : 'Name',
36384     valueText : 'Value',
36385     
36386     dateFormat : 'm/j/Y',
36387     
36388     
36389     renderDate : function(dateVal){
36390         return dateVal.dateFormat(this.dateFormat);
36391     },
36392
36393     renderBool : function(bVal){
36394         return bVal ? 'true' : 'false';
36395     },
36396
36397     isCellEditable : function(colIndex, rowIndex){
36398         return colIndex == 1;
36399     },
36400
36401     getRenderer : function(col){
36402         return col == 1 ?
36403             this.renderCellDelegate : this.renderPropDelegate;
36404     },
36405
36406     renderProp : function(v){
36407         return this.getPropertyName(v);
36408     },
36409
36410     renderCell : function(val){
36411         var rv = val;
36412         if(val instanceof Date){
36413             rv = this.renderDate(val);
36414         }else if(typeof val == 'boolean'){
36415             rv = this.renderBool(val);
36416         }
36417         return Roo.util.Format.htmlEncode(rv);
36418     },
36419
36420     getPropertyName : function(name){
36421         var pn = this.grid.propertyNames;
36422         return pn && pn[name] ? pn[name] : name;
36423     },
36424
36425     getCellEditor : function(colIndex, rowIndex){
36426         var p = this.store.getProperty(rowIndex);
36427         var n = p.data['name'], val = p.data['value'];
36428         
36429         if(typeof(this.grid.customEditors[n]) == 'string'){
36430             return this.editors[this.grid.customEditors[n]];
36431         }
36432         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36433             return this.grid.customEditors[n];
36434         }
36435         if(val instanceof Date){
36436             return this.editors['date'];
36437         }else if(typeof val == 'number'){
36438             return this.editors['number'];
36439         }else if(typeof val == 'boolean'){
36440             return this.editors['boolean'];
36441         }else{
36442             return this.editors['string'];
36443         }
36444     }
36445 });
36446
36447 /**
36448  * @class Roo.grid.PropertyGrid
36449  * @extends Roo.grid.EditorGrid
36450  * This class represents the  interface of a component based property grid control.
36451  * <br><br>Usage:<pre><code>
36452  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36453       
36454  });
36455  // set any options
36456  grid.render();
36457  * </code></pre>
36458   
36459  * @constructor
36460  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36461  * The container MUST have some type of size defined for the grid to fill. The container will be
36462  * automatically set to position relative if it isn't already.
36463  * @param {Object} config A config object that sets properties on this grid.
36464  */
36465 Roo.grid.PropertyGrid = function(container, config){
36466     config = config || {};
36467     var store = new Roo.grid.PropertyStore(this);
36468     this.store = store;
36469     var cm = new Roo.grid.PropertyColumnModel(this, store);
36470     store.store.sort('name', 'ASC');
36471     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36472         ds: store.store,
36473         cm: cm,
36474         enableColLock:false,
36475         enableColumnMove:false,
36476         stripeRows:false,
36477         trackMouseOver: false,
36478         clicksToEdit:1
36479     }, config));
36480     this.getGridEl().addClass('x-props-grid');
36481     this.lastEditRow = null;
36482     this.on('columnresize', this.onColumnResize, this);
36483     this.addEvents({
36484          /**
36485              * @event beforepropertychange
36486              * Fires before a property changes (return false to stop?)
36487              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36488              * @param {String} id Record Id
36489              * @param {String} newval New Value
36490          * @param {String} oldval Old Value
36491              */
36492         "beforepropertychange": true,
36493         /**
36494              * @event propertychange
36495              * Fires after a property changes
36496              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36497              * @param {String} id Record Id
36498              * @param {String} newval New Value
36499          * @param {String} oldval Old Value
36500              */
36501         "propertychange": true
36502     });
36503     this.customEditors = this.customEditors || {};
36504 };
36505 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36506     
36507      /**
36508      * @cfg {Object} customEditors map of colnames=> custom editors.
36509      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36510      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36511      * false disables editing of the field.
36512          */
36513     
36514       /**
36515      * @cfg {Object} propertyNames map of property Names to their displayed value
36516          */
36517     
36518     render : function(){
36519         Roo.grid.PropertyGrid.superclass.render.call(this);
36520         this.autoSize.defer(100, this);
36521     },
36522
36523     autoSize : function(){
36524         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36525         if(this.view){
36526             this.view.fitColumns();
36527         }
36528     },
36529
36530     onColumnResize : function(){
36531         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36532         this.autoSize();
36533     },
36534     /**
36535      * Sets the data for the Grid
36536      * accepts a Key => Value object of all the elements avaiable.
36537      * @param {Object} data  to appear in grid.
36538      */
36539     setSource : function(source){
36540         this.store.setSource(source);
36541         //this.autoSize();
36542     },
36543     /**
36544      * Gets all the data from the grid.
36545      * @return {Object} data  data stored in grid
36546      */
36547     getSource : function(){
36548         return this.store.getSource();
36549     }
36550 });/*
36551   
36552  * Licence LGPL
36553  
36554  */
36555  
36556 /**
36557  * @class Roo.grid.Calendar
36558  * @extends Roo.util.Grid
36559  * This class extends the Grid to provide a calendar widget
36560  * <br><br>Usage:<pre><code>
36561  var grid = new Roo.grid.Calendar("my-container-id", {
36562      ds: myDataStore,
36563      cm: myColModel,
36564      selModel: mySelectionModel,
36565      autoSizeColumns: true,
36566      monitorWindowResize: false,
36567      trackMouseOver: true
36568      eventstore : real data store..
36569  });
36570  // set any options
36571  grid.render();
36572   
36573   * @constructor
36574  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36575  * The container MUST have some type of size defined for the grid to fill. The container will be
36576  * automatically set to position relative if it isn't already.
36577  * @param {Object} config A config object that sets properties on this grid.
36578  */
36579 Roo.grid.Calendar = function(container, config){
36580         // initialize the container
36581         this.container = Roo.get(container);
36582         this.container.update("");
36583         this.container.setStyle("overflow", "hidden");
36584     this.container.addClass('x-grid-container');
36585
36586     this.id = this.container.id;
36587
36588     Roo.apply(this, config);
36589     // check and correct shorthanded configs
36590     
36591     var rows = [];
36592     var d =1;
36593     for (var r = 0;r < 6;r++) {
36594         
36595         rows[r]=[];
36596         for (var c =0;c < 7;c++) {
36597             rows[r][c]= '';
36598         }
36599     }
36600     if (this.eventStore) {
36601         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36602         this.eventStore.on('load',this.onLoad, this);
36603         this.eventStore.on('beforeload',this.clearEvents, this);
36604          
36605     }
36606     
36607     this.dataSource = new Roo.data.Store({
36608             proxy: new Roo.data.MemoryProxy(rows),
36609             reader: new Roo.data.ArrayReader({}, [
36610                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36611     });
36612
36613     this.dataSource.load();
36614     this.ds = this.dataSource;
36615     this.ds.xmodule = this.xmodule || false;
36616     
36617     
36618     var cellRender = function(v,x,r)
36619     {
36620         return String.format(
36621             '<div class="fc-day  fc-widget-content"><div>' +
36622                 '<div class="fc-event-container"></div>' +
36623                 '<div class="fc-day-number">{0}</div>'+
36624                 
36625                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36626             '</div></div>', v);
36627     
36628     }
36629     
36630     
36631     this.colModel = new Roo.grid.ColumnModel( [
36632         {
36633             xtype: 'ColumnModel',
36634             xns: Roo.grid,
36635             dataIndex : 'weekday0',
36636             header : 'Sunday',
36637             renderer : cellRender
36638         },
36639         {
36640             xtype: 'ColumnModel',
36641             xns: Roo.grid,
36642             dataIndex : 'weekday1',
36643             header : 'Monday',
36644             renderer : cellRender
36645         },
36646         {
36647             xtype: 'ColumnModel',
36648             xns: Roo.grid,
36649             dataIndex : 'weekday2',
36650             header : 'Tuesday',
36651             renderer : cellRender
36652         },
36653         {
36654             xtype: 'ColumnModel',
36655             xns: Roo.grid,
36656             dataIndex : 'weekday3',
36657             header : 'Wednesday',
36658             renderer : cellRender
36659         },
36660         {
36661             xtype: 'ColumnModel',
36662             xns: Roo.grid,
36663             dataIndex : 'weekday4',
36664             header : 'Thursday',
36665             renderer : cellRender
36666         },
36667         {
36668             xtype: 'ColumnModel',
36669             xns: Roo.grid,
36670             dataIndex : 'weekday5',
36671             header : 'Friday',
36672             renderer : cellRender
36673         },
36674         {
36675             xtype: 'ColumnModel',
36676             xns: Roo.grid,
36677             dataIndex : 'weekday6',
36678             header : 'Saturday',
36679             renderer : cellRender
36680         }
36681     ]);
36682     this.cm = this.colModel;
36683     this.cm.xmodule = this.xmodule || false;
36684  
36685         
36686           
36687     //this.selModel = new Roo.grid.CellSelectionModel();
36688     //this.sm = this.selModel;
36689     //this.selModel.init(this);
36690     
36691     
36692     if(this.width){
36693         this.container.setWidth(this.width);
36694     }
36695
36696     if(this.height){
36697         this.container.setHeight(this.height);
36698     }
36699     /** @private */
36700         this.addEvents({
36701         // raw events
36702         /**
36703          * @event click
36704          * The raw click event for the entire grid.
36705          * @param {Roo.EventObject} e
36706          */
36707         "click" : true,
36708         /**
36709          * @event dblclick
36710          * The raw dblclick event for the entire grid.
36711          * @param {Roo.EventObject} e
36712          */
36713         "dblclick" : true,
36714         /**
36715          * @event contextmenu
36716          * The raw contextmenu event for the entire grid.
36717          * @param {Roo.EventObject} e
36718          */
36719         "contextmenu" : true,
36720         /**
36721          * @event mousedown
36722          * The raw mousedown event for the entire grid.
36723          * @param {Roo.EventObject} e
36724          */
36725         "mousedown" : true,
36726         /**
36727          * @event mouseup
36728          * The raw mouseup event for the entire grid.
36729          * @param {Roo.EventObject} e
36730          */
36731         "mouseup" : true,
36732         /**
36733          * @event mouseover
36734          * The raw mouseover event for the entire grid.
36735          * @param {Roo.EventObject} e
36736          */
36737         "mouseover" : true,
36738         /**
36739          * @event mouseout
36740          * The raw mouseout event for the entire grid.
36741          * @param {Roo.EventObject} e
36742          */
36743         "mouseout" : true,
36744         /**
36745          * @event keypress
36746          * The raw keypress event for the entire grid.
36747          * @param {Roo.EventObject} e
36748          */
36749         "keypress" : true,
36750         /**
36751          * @event keydown
36752          * The raw keydown event for the entire grid.
36753          * @param {Roo.EventObject} e
36754          */
36755         "keydown" : true,
36756
36757         // custom events
36758
36759         /**
36760          * @event cellclick
36761          * Fires when a cell is clicked
36762          * @param {Grid} this
36763          * @param {Number} rowIndex
36764          * @param {Number} columnIndex
36765          * @param {Roo.EventObject} e
36766          */
36767         "cellclick" : true,
36768         /**
36769          * @event celldblclick
36770          * Fires when a cell is double clicked
36771          * @param {Grid} this
36772          * @param {Number} rowIndex
36773          * @param {Number} columnIndex
36774          * @param {Roo.EventObject} e
36775          */
36776         "celldblclick" : true,
36777         /**
36778          * @event rowclick
36779          * Fires when a row is clicked
36780          * @param {Grid} this
36781          * @param {Number} rowIndex
36782          * @param {Roo.EventObject} e
36783          */
36784         "rowclick" : true,
36785         /**
36786          * @event rowdblclick
36787          * Fires when a row is double clicked
36788          * @param {Grid} this
36789          * @param {Number} rowIndex
36790          * @param {Roo.EventObject} e
36791          */
36792         "rowdblclick" : true,
36793         /**
36794          * @event headerclick
36795          * Fires when a header is clicked
36796          * @param {Grid} this
36797          * @param {Number} columnIndex
36798          * @param {Roo.EventObject} e
36799          */
36800         "headerclick" : true,
36801         /**
36802          * @event headerdblclick
36803          * Fires when a header cell is double clicked
36804          * @param {Grid} this
36805          * @param {Number} columnIndex
36806          * @param {Roo.EventObject} e
36807          */
36808         "headerdblclick" : true,
36809         /**
36810          * @event rowcontextmenu
36811          * Fires when a row is right clicked
36812          * @param {Grid} this
36813          * @param {Number} rowIndex
36814          * @param {Roo.EventObject} e
36815          */
36816         "rowcontextmenu" : true,
36817         /**
36818          * @event cellcontextmenu
36819          * Fires when a cell is right clicked
36820          * @param {Grid} this
36821          * @param {Number} rowIndex
36822          * @param {Number} cellIndex
36823          * @param {Roo.EventObject} e
36824          */
36825          "cellcontextmenu" : true,
36826         /**
36827          * @event headercontextmenu
36828          * Fires when a header is right clicked
36829          * @param {Grid} this
36830          * @param {Number} columnIndex
36831          * @param {Roo.EventObject} e
36832          */
36833         "headercontextmenu" : true,
36834         /**
36835          * @event bodyscroll
36836          * Fires when the body element is scrolled
36837          * @param {Number} scrollLeft
36838          * @param {Number} scrollTop
36839          */
36840         "bodyscroll" : true,
36841         /**
36842          * @event columnresize
36843          * Fires when the user resizes a column
36844          * @param {Number} columnIndex
36845          * @param {Number} newSize
36846          */
36847         "columnresize" : true,
36848         /**
36849          * @event columnmove
36850          * Fires when the user moves a column
36851          * @param {Number} oldIndex
36852          * @param {Number} newIndex
36853          */
36854         "columnmove" : true,
36855         /**
36856          * @event startdrag
36857          * Fires when row(s) start being dragged
36858          * @param {Grid} this
36859          * @param {Roo.GridDD} dd The drag drop object
36860          * @param {event} e The raw browser event
36861          */
36862         "startdrag" : true,
36863         /**
36864          * @event enddrag
36865          * Fires when a drag operation is complete
36866          * @param {Grid} this
36867          * @param {Roo.GridDD} dd The drag drop object
36868          * @param {event} e The raw browser event
36869          */
36870         "enddrag" : true,
36871         /**
36872          * @event dragdrop
36873          * Fires when dragged row(s) are dropped on a valid DD target
36874          * @param {Grid} this
36875          * @param {Roo.GridDD} dd The drag drop object
36876          * @param {String} targetId The target drag drop object
36877          * @param {event} e The raw browser event
36878          */
36879         "dragdrop" : true,
36880         /**
36881          * @event dragover
36882          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36883          * @param {Grid} this
36884          * @param {Roo.GridDD} dd The drag drop object
36885          * @param {String} targetId The target drag drop object
36886          * @param {event} e The raw browser event
36887          */
36888         "dragover" : true,
36889         /**
36890          * @event dragenter
36891          *  Fires when the dragged row(s) first cross another DD target while being dragged
36892          * @param {Grid} this
36893          * @param {Roo.GridDD} dd The drag drop object
36894          * @param {String} targetId The target drag drop object
36895          * @param {event} e The raw browser event
36896          */
36897         "dragenter" : true,
36898         /**
36899          * @event dragout
36900          * Fires when the dragged row(s) leave another DD target while being dragged
36901          * @param {Grid} this
36902          * @param {Roo.GridDD} dd The drag drop object
36903          * @param {String} targetId The target drag drop object
36904          * @param {event} e The raw browser event
36905          */
36906         "dragout" : true,
36907         /**
36908          * @event rowclass
36909          * Fires when a row is rendered, so you can change add a style to it.
36910          * @param {GridView} gridview   The grid view
36911          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36912          */
36913         'rowclass' : true,
36914
36915         /**
36916          * @event render
36917          * Fires when the grid is rendered
36918          * @param {Grid} grid
36919          */
36920         'render' : true,
36921             /**
36922              * @event select
36923              * Fires when a date is selected
36924              * @param {DatePicker} this
36925              * @param {Date} date The selected date
36926              */
36927         'select': true,
36928         /**
36929              * @event monthchange
36930              * Fires when the displayed month changes 
36931              * @param {DatePicker} this
36932              * @param {Date} date The selected month
36933              */
36934         'monthchange': true,
36935         /**
36936              * @event evententer
36937              * Fires when mouse over an event
36938              * @param {Calendar} this
36939              * @param {event} Event
36940              */
36941         'evententer': true,
36942         /**
36943              * @event eventleave
36944              * Fires when the mouse leaves an
36945              * @param {Calendar} this
36946              * @param {event}
36947              */
36948         'eventleave': true,
36949         /**
36950              * @event eventclick
36951              * Fires when the mouse click an
36952              * @param {Calendar} this
36953              * @param {event}
36954              */
36955         'eventclick': true,
36956         /**
36957              * @event eventrender
36958              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36959              * @param {Calendar} this
36960              * @param {data} data to be modified
36961              */
36962         'eventrender': true
36963         
36964     });
36965
36966     Roo.grid.Grid.superclass.constructor.call(this);
36967     this.on('render', function() {
36968         this.view.el.addClass('x-grid-cal'); 
36969         
36970         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36971
36972     },this);
36973     
36974     if (!Roo.grid.Calendar.style) {
36975         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36976             
36977             
36978             '.x-grid-cal .x-grid-col' :  {
36979                 height: 'auto !important',
36980                 'vertical-align': 'top'
36981             },
36982             '.x-grid-cal  .fc-event-hori' : {
36983                 height: '14px'
36984             }
36985              
36986             
36987         }, Roo.id());
36988     }
36989
36990     
36991     
36992 };
36993 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36994     /**
36995      * @cfg {Store} eventStore The store that loads events.
36996      */
36997     eventStore : 25,
36998
36999      
37000     activeDate : false,
37001     startDay : 0,
37002     autoWidth : true,
37003     monitorWindowResize : false,
37004
37005     
37006     resizeColumns : function() {
37007         var col = (this.view.el.getWidth() / 7) - 3;
37008         // loop through cols, and setWidth
37009         for(var i =0 ; i < 7 ; i++){
37010             this.cm.setColumnWidth(i, col);
37011         }
37012     },
37013      setDate :function(date) {
37014         
37015         Roo.log('setDate?');
37016         
37017         this.resizeColumns();
37018         var vd = this.activeDate;
37019         this.activeDate = date;
37020 //        if(vd && this.el){
37021 //            var t = date.getTime();
37022 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37023 //                Roo.log('using add remove');
37024 //                
37025 //                this.fireEvent('monthchange', this, date);
37026 //                
37027 //                this.cells.removeClass("fc-state-highlight");
37028 //                this.cells.each(function(c){
37029 //                   if(c.dateValue == t){
37030 //                       c.addClass("fc-state-highlight");
37031 //                       setTimeout(function(){
37032 //                            try{c.dom.firstChild.focus();}catch(e){}
37033 //                       }, 50);
37034 //                       return false;
37035 //                   }
37036 //                   return true;
37037 //                });
37038 //                return;
37039 //            }
37040 //        }
37041         
37042         var days = date.getDaysInMonth();
37043         
37044         var firstOfMonth = date.getFirstDateOfMonth();
37045         var startingPos = firstOfMonth.getDay()-this.startDay;
37046         
37047         if(startingPos < this.startDay){
37048             startingPos += 7;
37049         }
37050         
37051         var pm = date.add(Date.MONTH, -1);
37052         var prevStart = pm.getDaysInMonth()-startingPos;
37053 //        
37054         
37055         
37056         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37057         
37058         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37059         //this.cells.addClassOnOver('fc-state-hover');
37060         
37061         var cells = this.cells.elements;
37062         var textEls = this.textNodes;
37063         
37064         //Roo.each(cells, function(cell){
37065         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37066         //});
37067         
37068         days += startingPos;
37069
37070         // convert everything to numbers so it's fast
37071         var day = 86400000;
37072         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37073         //Roo.log(d);
37074         //Roo.log(pm);
37075         //Roo.log(prevStart);
37076         
37077         var today = new Date().clearTime().getTime();
37078         var sel = date.clearTime().getTime();
37079         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37080         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37081         var ddMatch = this.disabledDatesRE;
37082         var ddText = this.disabledDatesText;
37083         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37084         var ddaysText = this.disabledDaysText;
37085         var format = this.format;
37086         
37087         var setCellClass = function(cal, cell){
37088             
37089             //Roo.log('set Cell Class');
37090             cell.title = "";
37091             var t = d.getTime();
37092             
37093             //Roo.log(d);
37094             
37095             
37096             cell.dateValue = t;
37097             if(t == today){
37098                 cell.className += " fc-today";
37099                 cell.className += " fc-state-highlight";
37100                 cell.title = cal.todayText;
37101             }
37102             if(t == sel){
37103                 // disable highlight in other month..
37104                 cell.className += " fc-state-highlight";
37105                 
37106             }
37107             // disabling
37108             if(t < min) {
37109                 //cell.className = " fc-state-disabled";
37110                 cell.title = cal.minText;
37111                 return;
37112             }
37113             if(t > max) {
37114                 //cell.className = " fc-state-disabled";
37115                 cell.title = cal.maxText;
37116                 return;
37117             }
37118             if(ddays){
37119                 if(ddays.indexOf(d.getDay()) != -1){
37120                     // cell.title = ddaysText;
37121                    // cell.className = " fc-state-disabled";
37122                 }
37123             }
37124             if(ddMatch && format){
37125                 var fvalue = d.dateFormat(format);
37126                 if(ddMatch.test(fvalue)){
37127                     cell.title = ddText.replace("%0", fvalue);
37128                    cell.className = " fc-state-disabled";
37129                 }
37130             }
37131             
37132             if (!cell.initialClassName) {
37133                 cell.initialClassName = cell.dom.className;
37134             }
37135             
37136             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37137         };
37138
37139         var i = 0;
37140         
37141         for(; i < startingPos; i++) {
37142             cells[i].dayName =  (++prevStart);
37143             Roo.log(textEls[i]);
37144             d.setDate(d.getDate()+1);
37145             
37146             //cells[i].className = "fc-past fc-other-month";
37147             setCellClass(this, cells[i]);
37148         }
37149         
37150         var intDay = 0;
37151         
37152         for(; i < days; i++){
37153             intDay = i - startingPos + 1;
37154             cells[i].dayName =  (intDay);
37155             d.setDate(d.getDate()+1);
37156             
37157             cells[i].className = ''; // "x-date-active";
37158             setCellClass(this, cells[i]);
37159         }
37160         var extraDays = 0;
37161         
37162         for(; i < 42; i++) {
37163             //textEls[i].innerHTML = (++extraDays);
37164             
37165             d.setDate(d.getDate()+1);
37166             cells[i].dayName = (++extraDays);
37167             cells[i].className = "fc-future fc-other-month";
37168             setCellClass(this, cells[i]);
37169         }
37170         
37171         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37172         
37173         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37174         
37175         // this will cause all the cells to mis
37176         var rows= [];
37177         var i =0;
37178         for (var r = 0;r < 6;r++) {
37179             for (var c =0;c < 7;c++) {
37180                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37181             }    
37182         }
37183         
37184         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37185         for(i=0;i<cells.length;i++) {
37186             
37187             this.cells.elements[i].dayName = cells[i].dayName ;
37188             this.cells.elements[i].className = cells[i].className;
37189             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37190             this.cells.elements[i].title = cells[i].title ;
37191             this.cells.elements[i].dateValue = cells[i].dateValue ;
37192         }
37193         
37194         
37195         
37196         
37197         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37198         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37199         
37200         ////if(totalRows != 6){
37201             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37202            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37203        // }
37204         
37205         this.fireEvent('monthchange', this, date);
37206         
37207         
37208     },
37209  /**
37210      * Returns the grid's SelectionModel.
37211      * @return {SelectionModel}
37212      */
37213     getSelectionModel : function(){
37214         if(!this.selModel){
37215             this.selModel = new Roo.grid.CellSelectionModel();
37216         }
37217         return this.selModel;
37218     },
37219
37220     load: function() {
37221         this.eventStore.load()
37222         
37223         
37224         
37225     },
37226     
37227     findCell : function(dt) {
37228         dt = dt.clearTime().getTime();
37229         var ret = false;
37230         this.cells.each(function(c){
37231             //Roo.log("check " +c.dateValue + '?=' + dt);
37232             if(c.dateValue == dt){
37233                 ret = c;
37234                 return false;
37235             }
37236             return true;
37237         });
37238         
37239         return ret;
37240     },
37241     
37242     findCells : function(rec) {
37243         var s = rec.data.start_dt.clone().clearTime().getTime();
37244        // Roo.log(s);
37245         var e= rec.data.end_dt.clone().clearTime().getTime();
37246        // Roo.log(e);
37247         var ret = [];
37248         this.cells.each(function(c){
37249              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37250             
37251             if(c.dateValue > e){
37252                 return ;
37253             }
37254             if(c.dateValue < s){
37255                 return ;
37256             }
37257             ret.push(c);
37258         });
37259         
37260         return ret;    
37261     },
37262     
37263     findBestRow: function(cells)
37264     {
37265         var ret = 0;
37266         
37267         for (var i =0 ; i < cells.length;i++) {
37268             ret  = Math.max(cells[i].rows || 0,ret);
37269         }
37270         return ret;
37271         
37272     },
37273     
37274     
37275     addItem : function(rec)
37276     {
37277         // look for vertical location slot in
37278         var cells = this.findCells(rec);
37279         
37280         rec.row = this.findBestRow(cells);
37281         
37282         // work out the location.
37283         
37284         var crow = false;
37285         var rows = [];
37286         for(var i =0; i < cells.length; i++) {
37287             if (!crow) {
37288                 crow = {
37289                     start : cells[i],
37290                     end :  cells[i]
37291                 };
37292                 continue;
37293             }
37294             if (crow.start.getY() == cells[i].getY()) {
37295                 // on same row.
37296                 crow.end = cells[i];
37297                 continue;
37298             }
37299             // different row.
37300             rows.push(crow);
37301             crow = {
37302                 start: cells[i],
37303                 end : cells[i]
37304             };
37305             
37306         }
37307         
37308         rows.push(crow);
37309         rec.els = [];
37310         rec.rows = rows;
37311         rec.cells = cells;
37312         for (var i = 0; i < cells.length;i++) {
37313             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37314             
37315         }
37316         
37317         
37318     },
37319     
37320     clearEvents: function() {
37321         
37322         if (!this.eventStore.getCount()) {
37323             return;
37324         }
37325         // reset number of rows in cells.
37326         Roo.each(this.cells.elements, function(c){
37327             c.rows = 0;
37328         });
37329         
37330         this.eventStore.each(function(e) {
37331             this.clearEvent(e);
37332         },this);
37333         
37334     },
37335     
37336     clearEvent : function(ev)
37337     {
37338         if (ev.els) {
37339             Roo.each(ev.els, function(el) {
37340                 el.un('mouseenter' ,this.onEventEnter, this);
37341                 el.un('mouseleave' ,this.onEventLeave, this);
37342                 el.remove();
37343             },this);
37344             ev.els = [];
37345         }
37346     },
37347     
37348     
37349     renderEvent : function(ev,ctr) {
37350         if (!ctr) {
37351              ctr = this.view.el.select('.fc-event-container',true).first();
37352         }
37353         
37354          
37355         this.clearEvent(ev);
37356             //code
37357        
37358         
37359         
37360         ev.els = [];
37361         var cells = ev.cells;
37362         var rows = ev.rows;
37363         this.fireEvent('eventrender', this, ev);
37364         
37365         for(var i =0; i < rows.length; i++) {
37366             
37367             cls = '';
37368             if (i == 0) {
37369                 cls += ' fc-event-start';
37370             }
37371             if ((i+1) == rows.length) {
37372                 cls += ' fc-event-end';
37373             }
37374             
37375             //Roo.log(ev.data);
37376             // how many rows should it span..
37377             var cg = this.eventTmpl.append(ctr,Roo.apply({
37378                 fccls : cls
37379                 
37380             }, ev.data) , true);
37381             
37382             
37383             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37384             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37385             cg.on('click', this.onEventClick, this, ev);
37386             
37387             ev.els.push(cg);
37388             
37389             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37390             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37391             //Roo.log(cg);
37392              
37393             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37394             cg.setWidth(ebox.right - sbox.x -2);
37395         }
37396     },
37397     
37398     renderEvents: function()
37399     {   
37400         // first make sure there is enough space..
37401         
37402         if (!this.eventTmpl) {
37403             this.eventTmpl = new Roo.Template(
37404                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37405                     '<div class="fc-event-inner">' +
37406                         '<span class="fc-event-time">{time}</span>' +
37407                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37408                     '</div>' +
37409                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37410                 '</div>'
37411             );
37412                 
37413         }
37414                
37415         
37416         
37417         this.cells.each(function(c) {
37418             //Roo.log(c.select('.fc-day-content div',true).first());
37419             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37420         });
37421         
37422         var ctr = this.view.el.select('.fc-event-container',true).first();
37423         
37424         var cls;
37425         this.eventStore.each(function(ev){
37426             
37427             this.renderEvent(ev);
37428              
37429              
37430         }, this);
37431         this.view.layout();
37432         
37433     },
37434     
37435     onEventEnter: function (e, el,event,d) {
37436         this.fireEvent('evententer', this, el, event);
37437     },
37438     
37439     onEventLeave: function (e, el,event,d) {
37440         this.fireEvent('eventleave', this, el, event);
37441     },
37442     
37443     onEventClick: function (e, el,event,d) {
37444         this.fireEvent('eventclick', this, el, event);
37445     },
37446     
37447     onMonthChange: function () {
37448         this.store.load();
37449     },
37450     
37451     onLoad: function () {
37452         
37453         //Roo.log('calendar onload');
37454 //         
37455         if(this.eventStore.getCount() > 0){
37456             
37457            
37458             
37459             this.eventStore.each(function(d){
37460                 
37461                 
37462                 // FIXME..
37463                 var add =   d.data;
37464                 if (typeof(add.end_dt) == 'undefined')  {
37465                     Roo.log("Missing End time in calendar data: ");
37466                     Roo.log(d);
37467                     return;
37468                 }
37469                 if (typeof(add.start_dt) == 'undefined')  {
37470                     Roo.log("Missing Start time in calendar data: ");
37471                     Roo.log(d);
37472                     return;
37473                 }
37474                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37475                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37476                 add.id = add.id || d.id;
37477                 add.title = add.title || '??';
37478                 
37479                 this.addItem(d);
37480                 
37481              
37482             },this);
37483         }
37484         
37485         this.renderEvents();
37486     }
37487     
37488
37489 });
37490 /*
37491  grid : {
37492                 xtype: 'Grid',
37493                 xns: Roo.grid,
37494                 listeners : {
37495                     render : function ()
37496                     {
37497                         _this.grid = this;
37498                         
37499                         if (!this.view.el.hasClass('course-timesheet')) {
37500                             this.view.el.addClass('course-timesheet');
37501                         }
37502                         if (this.tsStyle) {
37503                             this.ds.load({});
37504                             return; 
37505                         }
37506                         Roo.log('width');
37507                         Roo.log(_this.grid.view.el.getWidth());
37508                         
37509                         
37510                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37511                             '.course-timesheet .x-grid-row' : {
37512                                 height: '80px'
37513                             },
37514                             '.x-grid-row td' : {
37515                                 'vertical-align' : 0
37516                             },
37517                             '.course-edit-link' : {
37518                                 'color' : 'blue',
37519                                 'text-overflow' : 'ellipsis',
37520                                 'overflow' : 'hidden',
37521                                 'white-space' : 'nowrap',
37522                                 'cursor' : 'pointer'
37523                             },
37524                             '.sub-link' : {
37525                                 'color' : 'green'
37526                             },
37527                             '.de-act-sup-link' : {
37528                                 'color' : 'purple',
37529                                 'text-decoration' : 'line-through'
37530                             },
37531                             '.de-act-link' : {
37532                                 'color' : 'red',
37533                                 'text-decoration' : 'line-through'
37534                             },
37535                             '.course-timesheet .course-highlight' : {
37536                                 'border-top-style': 'dashed !important',
37537                                 'border-bottom-bottom': 'dashed !important'
37538                             },
37539                             '.course-timesheet .course-item' : {
37540                                 'font-family'   : 'tahoma, arial, helvetica',
37541                                 'font-size'     : '11px',
37542                                 'overflow'      : 'hidden',
37543                                 'padding-left'  : '10px',
37544                                 'padding-right' : '10px',
37545                                 'padding-top' : '10px' 
37546                             }
37547                             
37548                         }, Roo.id());
37549                                 this.ds.load({});
37550                     }
37551                 },
37552                 autoWidth : true,
37553                 monitorWindowResize : false,
37554                 cellrenderer : function(v,x,r)
37555                 {
37556                     return v;
37557                 },
37558                 sm : {
37559                     xtype: 'CellSelectionModel',
37560                     xns: Roo.grid
37561                 },
37562                 dataSource : {
37563                     xtype: 'Store',
37564                     xns: Roo.data,
37565                     listeners : {
37566                         beforeload : function (_self, options)
37567                         {
37568                             options.params = options.params || {};
37569                             options.params._month = _this.monthField.getValue();
37570                             options.params.limit = 9999;
37571                             options.params['sort'] = 'when_dt';    
37572                             options.params['dir'] = 'ASC';    
37573                             this.proxy.loadResponse = this.loadResponse;
37574                             Roo.log("load?");
37575                             //this.addColumns();
37576                         },
37577                         load : function (_self, records, options)
37578                         {
37579                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37580                                 // if you click on the translation.. you can edit it...
37581                                 var el = Roo.get(this);
37582                                 var id = el.dom.getAttribute('data-id');
37583                                 var d = el.dom.getAttribute('data-date');
37584                                 var t = el.dom.getAttribute('data-time');
37585                                 //var id = this.child('span').dom.textContent;
37586                                 
37587                                 //Roo.log(this);
37588                                 Pman.Dialog.CourseCalendar.show({
37589                                     id : id,
37590                                     when_d : d,
37591                                     when_t : t,
37592                                     productitem_active : id ? 1 : 0
37593                                 }, function() {
37594                                     _this.grid.ds.load({});
37595                                 });
37596                            
37597                            });
37598                            
37599                            _this.panel.fireEvent('resize', [ '', '' ]);
37600                         }
37601                     },
37602                     loadResponse : function(o, success, response){
37603                             // this is overridden on before load..
37604                             
37605                             Roo.log("our code?");       
37606                             //Roo.log(success);
37607                             //Roo.log(response)
37608                             delete this.activeRequest;
37609                             if(!success){
37610                                 this.fireEvent("loadexception", this, o, response);
37611                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37612                                 return;
37613                             }
37614                             var result;
37615                             try {
37616                                 result = o.reader.read(response);
37617                             }catch(e){
37618                                 Roo.log("load exception?");
37619                                 this.fireEvent("loadexception", this, o, response, e);
37620                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37621                                 return;
37622                             }
37623                             Roo.log("ready...");        
37624                             // loop through result.records;
37625                             // and set this.tdate[date] = [] << array of records..
37626                             _this.tdata  = {};
37627                             Roo.each(result.records, function(r){
37628                                 //Roo.log(r.data);
37629                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37630                                     _this.tdata[r.data.when_dt.format('j')] = [];
37631                                 }
37632                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37633                             });
37634                             
37635                             //Roo.log(_this.tdata);
37636                             
37637                             result.records = [];
37638                             result.totalRecords = 6;
37639                     
37640                             // let's generate some duumy records for the rows.
37641                             //var st = _this.dateField.getValue();
37642                             
37643                             // work out monday..
37644                             //st = st.add(Date.DAY, -1 * st.format('w'));
37645                             
37646                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37647                             
37648                             var firstOfMonth = date.getFirstDayOfMonth();
37649                             var days = date.getDaysInMonth();
37650                             var d = 1;
37651                             var firstAdded = false;
37652                             for (var i = 0; i < result.totalRecords ; i++) {
37653                                 //var d= st.add(Date.DAY, i);
37654                                 var row = {};
37655                                 var added = 0;
37656                                 for(var w = 0 ; w < 7 ; w++){
37657                                     if(!firstAdded && firstOfMonth != w){
37658                                         continue;
37659                                     }
37660                                     if(d > days){
37661                                         continue;
37662                                     }
37663                                     firstAdded = true;
37664                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37665                                     row['weekday'+w] = String.format(
37666                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37667                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37668                                                     d,
37669                                                     date.format('Y-m-')+dd
37670                                                 );
37671                                     added++;
37672                                     if(typeof(_this.tdata[d]) != 'undefined'){
37673                                         Roo.each(_this.tdata[d], function(r){
37674                                             var is_sub = '';
37675                                             var deactive = '';
37676                                             var id = r.id;
37677                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37678                                             if(r.parent_id*1>0){
37679                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37680                                                 id = r.parent_id;
37681                                             }
37682                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37683                                                 deactive = 'de-act-link';
37684                                             }
37685                                             
37686                                             row['weekday'+w] += String.format(
37687                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37688                                                     id, //0
37689                                                     r.product_id_name, //1
37690                                                     r.when_dt.format('h:ia'), //2
37691                                                     is_sub, //3
37692                                                     deactive, //4
37693                                                     desc // 5
37694                                             );
37695                                         });
37696                                     }
37697                                     d++;
37698                                 }
37699                                 
37700                                 // only do this if something added..
37701                                 if(added > 0){ 
37702                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37703                                 }
37704                                 
37705                                 
37706                                 // push it twice. (second one with an hour..
37707                                 
37708                             }
37709                             //Roo.log(result);
37710                             this.fireEvent("load", this, o, o.request.arg);
37711                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37712                         },
37713                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37714                     proxy : {
37715                         xtype: 'HttpProxy',
37716                         xns: Roo.data,
37717                         method : 'GET',
37718                         url : baseURL + '/Roo/Shop_course.php'
37719                     },
37720                     reader : {
37721                         xtype: 'JsonReader',
37722                         xns: Roo.data,
37723                         id : 'id',
37724                         fields : [
37725                             {
37726                                 'name': 'id',
37727                                 'type': 'int'
37728                             },
37729                             {
37730                                 'name': 'when_dt',
37731                                 'type': 'string'
37732                             },
37733                             {
37734                                 'name': 'end_dt',
37735                                 'type': 'string'
37736                             },
37737                             {
37738                                 'name': 'parent_id',
37739                                 'type': 'int'
37740                             },
37741                             {
37742                                 'name': 'product_id',
37743                                 'type': 'int'
37744                             },
37745                             {
37746                                 'name': 'productitem_id',
37747                                 'type': 'int'
37748                             },
37749                             {
37750                                 'name': 'guid',
37751                                 'type': 'int'
37752                             }
37753                         ]
37754                     }
37755                 },
37756                 toolbar : {
37757                     xtype: 'Toolbar',
37758                     xns: Roo,
37759                     items : [
37760                         {
37761                             xtype: 'Button',
37762                             xns: Roo.Toolbar,
37763                             listeners : {
37764                                 click : function (_self, e)
37765                                 {
37766                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37767                                     sd.setMonth(sd.getMonth()-1);
37768                                     _this.monthField.setValue(sd.format('Y-m-d'));
37769                                     _this.grid.ds.load({});
37770                                 }
37771                             },
37772                             text : "Back"
37773                         },
37774                         {
37775                             xtype: 'Separator',
37776                             xns: Roo.Toolbar
37777                         },
37778                         {
37779                             xtype: 'MonthField',
37780                             xns: Roo.form,
37781                             listeners : {
37782                                 render : function (_self)
37783                                 {
37784                                     _this.monthField = _self;
37785                                    // _this.monthField.set  today
37786                                 },
37787                                 select : function (combo, date)
37788                                 {
37789                                     _this.grid.ds.load({});
37790                                 }
37791                             },
37792                             value : (function() { return new Date(); })()
37793                         },
37794                         {
37795                             xtype: 'Separator',
37796                             xns: Roo.Toolbar
37797                         },
37798                         {
37799                             xtype: 'TextItem',
37800                             xns: Roo.Toolbar,
37801                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37802                         },
37803                         {
37804                             xtype: 'Fill',
37805                             xns: Roo.Toolbar
37806                         },
37807                         {
37808                             xtype: 'Button',
37809                             xns: Roo.Toolbar,
37810                             listeners : {
37811                                 click : function (_self, e)
37812                                 {
37813                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37814                                     sd.setMonth(sd.getMonth()+1);
37815                                     _this.monthField.setValue(sd.format('Y-m-d'));
37816                                     _this.grid.ds.load({});
37817                                 }
37818                             },
37819                             text : "Next"
37820                         }
37821                     ]
37822                 },
37823                  
37824             }
37825         };
37826         
37827         *//*
37828  * Based on:
37829  * Ext JS Library 1.1.1
37830  * Copyright(c) 2006-2007, Ext JS, LLC.
37831  *
37832  * Originally Released Under LGPL - original licence link has changed is not relivant.
37833  *
37834  * Fork - LGPL
37835  * <script type="text/javascript">
37836  */
37837  
37838 /**
37839  * @class Roo.LoadMask
37840  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37841  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37842  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37843  * element's UpdateManager load indicator and will be destroyed after the initial load.
37844  * @constructor
37845  * Create a new LoadMask
37846  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37847  * @param {Object} config The config object
37848  */
37849 Roo.LoadMask = function(el, config){
37850     this.el = Roo.get(el);
37851     Roo.apply(this, config);
37852     if(this.store){
37853         this.store.on('beforeload', this.onBeforeLoad, this);
37854         this.store.on('load', this.onLoad, this);
37855         this.store.on('loadexception', this.onLoadException, this);
37856         this.removeMask = false;
37857     }else{
37858         var um = this.el.getUpdateManager();
37859         um.showLoadIndicator = false; // disable the default indicator
37860         um.on('beforeupdate', this.onBeforeLoad, this);
37861         um.on('update', this.onLoad, this);
37862         um.on('failure', this.onLoad, this);
37863         this.removeMask = true;
37864     }
37865 };
37866
37867 Roo.LoadMask.prototype = {
37868     /**
37869      * @cfg {Boolean} removeMask
37870      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37871      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37872      */
37873     /**
37874      * @cfg {String} msg
37875      * The text to display in a centered loading message box (defaults to 'Loading...')
37876      */
37877     msg : 'Loading...',
37878     /**
37879      * @cfg {String} msgCls
37880      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37881      */
37882     msgCls : 'x-mask-loading',
37883
37884     /**
37885      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37886      * @type Boolean
37887      */
37888     disabled: false,
37889
37890     /**
37891      * Disables the mask to prevent it from being displayed
37892      */
37893     disable : function(){
37894        this.disabled = true;
37895     },
37896
37897     /**
37898      * Enables the mask so that it can be displayed
37899      */
37900     enable : function(){
37901         this.disabled = false;
37902     },
37903     
37904     onLoadException : function()
37905     {
37906         Roo.log(arguments);
37907         
37908         if (typeof(arguments[3]) != 'undefined') {
37909             Roo.MessageBox.alert("Error loading",arguments[3]);
37910         } 
37911         /*
37912         try {
37913             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37914                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37915             }   
37916         } catch(e) {
37917             
37918         }
37919         */
37920     
37921         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37922     },
37923     // private
37924     onLoad : function()
37925     {
37926         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37927     },
37928
37929     // private
37930     onBeforeLoad : function(){
37931         if(!this.disabled){
37932             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37933         }
37934     },
37935
37936     // private
37937     destroy : function(){
37938         if(this.store){
37939             this.store.un('beforeload', this.onBeforeLoad, this);
37940             this.store.un('load', this.onLoad, this);
37941             this.store.un('loadexception', this.onLoadException, this);
37942         }else{
37943             var um = this.el.getUpdateManager();
37944             um.un('beforeupdate', this.onBeforeLoad, this);
37945             um.un('update', this.onLoad, this);
37946             um.un('failure', this.onLoad, this);
37947         }
37948     }
37949 };/*
37950  * Based on:
37951  * Ext JS Library 1.1.1
37952  * Copyright(c) 2006-2007, Ext JS, LLC.
37953  *
37954  * Originally Released Under LGPL - original licence link has changed is not relivant.
37955  *
37956  * Fork - LGPL
37957  * <script type="text/javascript">
37958  */
37959
37960
37961 /**
37962  * @class Roo.XTemplate
37963  * @extends Roo.Template
37964  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37965 <pre><code>
37966 var t = new Roo.XTemplate(
37967         '&lt;select name="{name}"&gt;',
37968                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37969         '&lt;/select&gt;'
37970 );
37971  
37972 // then append, applying the master template values
37973  </code></pre>
37974  *
37975  * Supported features:
37976  *
37977  *  Tags:
37978
37979 <pre><code>
37980       {a_variable} - output encoded.
37981       {a_variable.format:("Y-m-d")} - call a method on the variable
37982       {a_variable:raw} - unencoded output
37983       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37984       {a_variable:this.method_on_template(...)} - call a method on the template object.
37985  
37986 </code></pre>
37987  *  The tpl tag:
37988 <pre><code>
37989         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37990         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37991         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37992         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37993   
37994         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37995         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37996 </code></pre>
37997  *      
37998  */
37999 Roo.XTemplate = function()
38000 {
38001     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38002     if (this.html) {
38003         this.compile();
38004     }
38005 };
38006
38007
38008 Roo.extend(Roo.XTemplate, Roo.Template, {
38009
38010     /**
38011      * The various sub templates
38012      */
38013     tpls : false,
38014     /**
38015      *
38016      * basic tag replacing syntax
38017      * WORD:WORD()
38018      *
38019      * // you can fake an object call by doing this
38020      *  x.t:(test,tesT) 
38021      * 
38022      */
38023     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38024
38025     /**
38026      * compile the template
38027      *
38028      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38029      *
38030      */
38031     compile: function()
38032     {
38033         var s = this.html;
38034      
38035         s = ['<tpl>', s, '</tpl>'].join('');
38036     
38037         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38038             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38039             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38040             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38041             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38042             m,
38043             id     = 0,
38044             tpls   = [];
38045     
38046         while(true == !!(m = s.match(re))){
38047             var forMatch   = m[0].match(nameRe),
38048                 ifMatch   = m[0].match(ifRe),
38049                 execMatch   = m[0].match(execRe),
38050                 namedMatch   = m[0].match(namedRe),
38051                 
38052                 exp  = null, 
38053                 fn   = null,
38054                 exec = null,
38055                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38056                 
38057             if (ifMatch) {
38058                 // if - puts fn into test..
38059                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38060                 if(exp){
38061                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38062                 }
38063             }
38064             
38065             if (execMatch) {
38066                 // exec - calls a function... returns empty if true is  returned.
38067                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38068                 if(exp){
38069                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38070                 }
38071             }
38072             
38073             
38074             if (name) {
38075                 // for = 
38076                 switch(name){
38077                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38078                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38079                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38080                 }
38081             }
38082             var uid = namedMatch ? namedMatch[1] : id;
38083             
38084             
38085             tpls.push({
38086                 id:     namedMatch ? namedMatch[1] : id,
38087                 target: name,
38088                 exec:   exec,
38089                 test:   fn,
38090                 body:   m[1] || ''
38091             });
38092             if (namedMatch) {
38093                 s = s.replace(m[0], '');
38094             } else { 
38095                 s = s.replace(m[0], '{xtpl'+ id + '}');
38096             }
38097             ++id;
38098         }
38099         this.tpls = [];
38100         for(var i = tpls.length-1; i >= 0; --i){
38101             this.compileTpl(tpls[i]);
38102             this.tpls[tpls[i].id] = tpls[i];
38103         }
38104         this.master = tpls[tpls.length-1];
38105         return this;
38106     },
38107     /**
38108      * same as applyTemplate, except it's done to one of the subTemplates
38109      * when using named templates, you can do:
38110      *
38111      * var str = pl.applySubTemplate('your-name', values);
38112      *
38113      * 
38114      * @param {Number} id of the template
38115      * @param {Object} values to apply to template
38116      * @param {Object} parent (normaly the instance of this object)
38117      */
38118     applySubTemplate : function(id, values, parent)
38119     {
38120         
38121         
38122         var t = this.tpls[id];
38123         
38124         
38125         try { 
38126             if(t.test && !t.test.call(this, values, parent)){
38127                 return '';
38128             }
38129         } catch(e) {
38130             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38131             Roo.log(e.toString());
38132             Roo.log(t.test);
38133             return ''
38134         }
38135         try { 
38136             
38137             if(t.exec && t.exec.call(this, values, parent)){
38138                 return '';
38139             }
38140         } catch(e) {
38141             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38142             Roo.log(e.toString());
38143             Roo.log(t.exec);
38144             return ''
38145         }
38146         try {
38147             var vs = t.target ? t.target.call(this, values, parent) : values;
38148             parent = t.target ? values : parent;
38149             if(t.target && vs instanceof Array){
38150                 var buf = [];
38151                 for(var i = 0, len = vs.length; i < len; i++){
38152                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38153                 }
38154                 return buf.join('');
38155             }
38156             return t.compiled.call(this, vs, parent);
38157         } catch (e) {
38158             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38159             Roo.log(e.toString());
38160             Roo.log(t.compiled);
38161             return '';
38162         }
38163     },
38164
38165     compileTpl : function(tpl)
38166     {
38167         var fm = Roo.util.Format;
38168         var useF = this.disableFormats !== true;
38169         var sep = Roo.isGecko ? "+" : ",";
38170         var undef = function(str) {
38171             Roo.log("Property not found :"  + str);
38172             return '';
38173         };
38174         
38175         var fn = function(m, name, format, args)
38176         {
38177             //Roo.log(arguments);
38178             args = args ? args.replace(/\\'/g,"'") : args;
38179             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38180             if (typeof(format) == 'undefined') {
38181                 format= 'htmlEncode';
38182             }
38183             if (format == 'raw' ) {
38184                 format = false;
38185             }
38186             
38187             if(name.substr(0, 4) == 'xtpl'){
38188                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38189             }
38190             
38191             // build an array of options to determine if value is undefined..
38192             
38193             // basically get 'xxxx.yyyy' then do
38194             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38195             //    (function () { Roo.log("Property not found"); return ''; })() :
38196             //    ......
38197             
38198             var udef_ar = [];
38199             var lookfor = '';
38200             Roo.each(name.split('.'), function(st) {
38201                 lookfor += (lookfor.length ? '.': '') + st;
38202                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38203             });
38204             
38205             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38206             
38207             
38208             if(format && useF){
38209                 
38210                 args = args ? ',' + args : "";
38211                  
38212                 if(format.substr(0, 5) != "this."){
38213                     format = "fm." + format + '(';
38214                 }else{
38215                     format = 'this.call("'+ format.substr(5) + '", ';
38216                     args = ", values";
38217                 }
38218                 
38219                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38220             }
38221              
38222             if (args.length) {
38223                 // called with xxyx.yuu:(test,test)
38224                 // change to ()
38225                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38226             }
38227             // raw.. - :raw modifier..
38228             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38229             
38230         };
38231         var body;
38232         // branched to use + in gecko and [].join() in others
38233         if(Roo.isGecko){
38234             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38235                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38236                     "';};};";
38237         }else{
38238             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38239             body.push(tpl.body.replace(/(\r\n|\n)/g,
38240                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38241             body.push("'].join('');};};");
38242             body = body.join('');
38243         }
38244         
38245         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38246        
38247         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38248         eval(body);
38249         
38250         return this;
38251     },
38252
38253     applyTemplate : function(values){
38254         return this.master.compiled.call(this, values, {});
38255         //var s = this.subs;
38256     },
38257
38258     apply : function(){
38259         return this.applyTemplate.apply(this, arguments);
38260     }
38261
38262  });
38263
38264 Roo.XTemplate.from = function(el){
38265     el = Roo.getDom(el);
38266     return new Roo.XTemplate(el.value || el.innerHTML);
38267 };