roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @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)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <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
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204             var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     // used when loading children.. @see loadDataFromChildren
2224     toLoadData: function(rec)
2225     {
2226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2227         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2228         
2229     }
2230     
2231     
2232 });/*
2233  * Based on:
2234  * Ext JS Library 1.1.1
2235  * Copyright(c) 2006-2007, Ext JS, LLC.
2236  *
2237  * Originally Released Under LGPL - original licence link has changed is not relivant.
2238  *
2239  * Fork - LGPL
2240  * <script type="text/javascript">
2241  */
2242
2243
2244 /**
2245  * @class Roo.data.Tree
2246  * @extends Roo.util.Observable
2247  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2248  * in the tree have most standard DOM functionality.
2249  * @constructor
2250  * @param {Node} root (optional) The root node
2251  */
2252 Roo.data.Tree = function(root){
2253    this.nodeHash = {};
2254    /**
2255     * The root node for this tree
2256     * @type Node
2257     */
2258    this.root = null;
2259    if(root){
2260        this.setRootNode(root);
2261    }
2262    this.addEvents({
2263        /**
2264         * @event append
2265         * Fires when a new child node is appended to a node in this tree.
2266         * @param {Tree} tree The owner tree
2267         * @param {Node} parent The parent node
2268         * @param {Node} node The newly appended node
2269         * @param {Number} index The index of the newly appended node
2270         */
2271        "append" : true,
2272        /**
2273         * @event remove
2274         * Fires when a child node is removed from a node in this tree.
2275         * @param {Tree} tree The owner tree
2276         * @param {Node} parent The parent node
2277         * @param {Node} node The child node removed
2278         */
2279        "remove" : true,
2280        /**
2281         * @event move
2282         * Fires when a node is moved to a new location in the tree
2283         * @param {Tree} tree The owner tree
2284         * @param {Node} node The node moved
2285         * @param {Node} oldParent The old parent of this node
2286         * @param {Node} newParent The new parent of this node
2287         * @param {Number} index The index it was moved to
2288         */
2289        "move" : true,
2290        /**
2291         * @event insert
2292         * Fires when a new child node is inserted in a node in this tree.
2293         * @param {Tree} tree The owner tree
2294         * @param {Node} parent The parent node
2295         * @param {Node} node The child node inserted
2296         * @param {Node} refNode The child node the node was inserted before
2297         */
2298        "insert" : true,
2299        /**
2300         * @event beforeappend
2301         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be appended
2305         */
2306        "beforeappend" : true,
2307        /**
2308         * @event beforeremove
2309         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2310         * @param {Tree} tree The owner tree
2311         * @param {Node} parent The parent node
2312         * @param {Node} node The child node to be removed
2313         */
2314        "beforeremove" : true,
2315        /**
2316         * @event beforemove
2317         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} node The node being moved
2320         * @param {Node} oldParent The parent of the node
2321         * @param {Node} newParent The new parent the node is moving to
2322         * @param {Number} index The index it is being moved to
2323         */
2324        "beforemove" : true,
2325        /**
2326         * @event beforeinsert
2327         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be inserted
2331         * @param {Node} refNode The child node the node is being inserted before
2332         */
2333        "beforeinsert" : true
2334    });
2335
2336     Roo.data.Tree.superclass.constructor.call(this);
2337 };
2338
2339 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2340     pathSeparator: "/",
2341
2342     proxyNodeEvent : function(){
2343         return this.fireEvent.apply(this, arguments);
2344     },
2345
2346     /**
2347      * Returns the root node for this tree.
2348      * @return {Node}
2349      */
2350     getRootNode : function(){
2351         return this.root;
2352     },
2353
2354     /**
2355      * Sets the root node for this tree.
2356      * @param {Node} node
2357      * @return {Node}
2358      */
2359     setRootNode : function(node){
2360         this.root = node;
2361         node.ownerTree = this;
2362         node.isRoot = true;
2363         this.registerNode(node);
2364         return node;
2365     },
2366
2367     /**
2368      * Gets a node in this tree by its id.
2369      * @param {String} id
2370      * @return {Node}
2371      */
2372     getNodeById : function(id){
2373         return this.nodeHash[id];
2374     },
2375
2376     registerNode : function(node){
2377         this.nodeHash[node.id] = node;
2378     },
2379
2380     unregisterNode : function(node){
2381         delete this.nodeHash[node.id];
2382     },
2383
2384     toString : function(){
2385         return "[Tree"+(this.id?" "+this.id:"")+"]";
2386     }
2387 });
2388
2389 /**
2390  * @class Roo.data.Node
2391  * @extends Roo.util.Observable
2392  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2393  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2394  * @constructor
2395  * @param {Object} attributes The attributes/config for the node
2396  */
2397 Roo.data.Node = function(attributes){
2398     /**
2399      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2400      * @type {Object}
2401      */
2402     this.attributes = attributes || {};
2403     this.leaf = this.attributes.leaf;
2404     /**
2405      * The node id. @type String
2406      */
2407     this.id = this.attributes.id;
2408     if(!this.id){
2409         this.id = Roo.id(null, "ynode-");
2410         this.attributes.id = this.id;
2411     }
2412      
2413     
2414     /**
2415      * All child nodes of this node. @type Array
2416      */
2417     this.childNodes = [];
2418     if(!this.childNodes.indexOf){ // indexOf is a must
2419         this.childNodes.indexOf = function(o){
2420             for(var i = 0, len = this.length; i < len; i++){
2421                 if(this[i] == o) {
2422                     return i;
2423                 }
2424             }
2425             return -1;
2426         };
2427     }
2428     /**
2429      * The parent node for this node. @type Node
2430      */
2431     this.parentNode = null;
2432     /**
2433      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.firstChild = null;
2436     /**
2437      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.lastChild = null;
2440     /**
2441      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.previousSibling = null;
2444     /**
2445      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.nextSibling = null;
2448
2449     this.addEvents({
2450        /**
2451         * @event append
2452         * Fires when a new child node is appended
2453         * @param {Tree} tree The owner tree
2454         * @param {Node} this This node
2455         * @param {Node} node The newly appended node
2456         * @param {Number} index The index of the newly appended node
2457         */
2458        "append" : true,
2459        /**
2460         * @event remove
2461         * Fires when a child node is removed
2462         * @param {Tree} tree The owner tree
2463         * @param {Node} this This node
2464         * @param {Node} node The removed node
2465         */
2466        "remove" : true,
2467        /**
2468         * @event move
2469         * Fires when this node is moved to a new location in the tree
2470         * @param {Tree} tree The owner tree
2471         * @param {Node} this This node
2472         * @param {Node} oldParent The old parent of this node
2473         * @param {Node} newParent The new parent of this node
2474         * @param {Number} index The index it was moved to
2475         */
2476        "move" : true,
2477        /**
2478         * @event insert
2479         * Fires when a new child node is inserted.
2480         * @param {Tree} tree The owner tree
2481         * @param {Node} this This node
2482         * @param {Node} node The child node inserted
2483         * @param {Node} refNode The child node the node was inserted before
2484         */
2485        "insert" : true,
2486        /**
2487         * @event beforeappend
2488         * Fires before a new child is appended, return false to cancel the append.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be appended
2492         */
2493        "beforeappend" : true,
2494        /**
2495         * @event beforeremove
2496         * Fires before a child is removed, return false to cancel the remove.
2497         * @param {Tree} tree The owner tree
2498         * @param {Node} this This node
2499         * @param {Node} node The child node to be removed
2500         */
2501        "beforeremove" : true,
2502        /**
2503         * @event beforemove
2504         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} oldParent The parent of this node
2508         * @param {Node} newParent The new parent this node is moving to
2509         * @param {Number} index The index it is being moved to
2510         */
2511        "beforemove" : true,
2512        /**
2513         * @event beforeinsert
2514         * Fires before a new child is inserted, return false to cancel the insert.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be inserted
2518         * @param {Node} refNode The child node the node is being inserted before
2519         */
2520        "beforeinsert" : true
2521    });
2522     this.listeners = this.attributes.listeners;
2523     Roo.data.Node.superclass.constructor.call(this);
2524 };
2525
2526 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2527     fireEvent : function(evtName){
2528         // first do standard event for this node
2529         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2530             return false;
2531         }
2532         // then bubble it up to the tree if the event wasn't cancelled
2533         var ot = this.getOwnerTree();
2534         if(ot){
2535             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2536                 return false;
2537             }
2538         }
2539         return true;
2540     },
2541
2542     /**
2543      * Returns true if this node is a leaf
2544      * @return {Boolean}
2545      */
2546     isLeaf : function(){
2547         return this.leaf === true;
2548     },
2549
2550     // private
2551     setFirstChild : function(node){
2552         this.firstChild = node;
2553     },
2554
2555     //private
2556     setLastChild : function(node){
2557         this.lastChild = node;
2558     },
2559
2560
2561     /**
2562      * Returns true if this node is the last child of its parent
2563      * @return {Boolean}
2564      */
2565     isLast : function(){
2566        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2567     },
2568
2569     /**
2570      * Returns true if this node is the first child of its parent
2571      * @return {Boolean}
2572      */
2573     isFirst : function(){
2574        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2575     },
2576
2577     hasChildNodes : function(){
2578         return !this.isLeaf() && this.childNodes.length > 0;
2579     },
2580
2581     /**
2582      * Insert node(s) as the last child node of this node.
2583      * @param {Node/Array} node The node or Array of nodes to append
2584      * @return {Node} The appended node if single append, or null if an array was passed
2585      */
2586     appendChild : function(node){
2587         var multi = false;
2588         if(node instanceof Array){
2589             multi = node;
2590         }else if(arguments.length > 1){
2591             multi = arguments;
2592         }
2593         
2594         // if passed an array or multiple args do them one by one
2595         if(multi){
2596             for(var i = 0, len = multi.length; i < len; i++) {
2597                 this.appendChild(multi[i]);
2598             }
2599         }else{
2600             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2601                 return false;
2602             }
2603             var index = this.childNodes.length;
2604             var oldParent = node.parentNode;
2605             // it's a move, make sure we move it cleanly
2606             if(oldParent){
2607                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2608                     return false;
2609                 }
2610                 oldParent.removeChild(node);
2611             }
2612             
2613             index = this.childNodes.length;
2614             if(index == 0){
2615                 this.setFirstChild(node);
2616             }
2617             this.childNodes.push(node);
2618             node.parentNode = this;
2619             var ps = this.childNodes[index-1];
2620             if(ps){
2621                 node.previousSibling = ps;
2622                 ps.nextSibling = node;
2623             }else{
2624                 node.previousSibling = null;
2625             }
2626             node.nextSibling = null;
2627             this.setLastChild(node);
2628             node.setOwnerTree(this.getOwnerTree());
2629             this.fireEvent("append", this.ownerTree, this, node, index);
2630             if(this.ownerTree) {
2631                 this.ownerTree.fireEvent("appendnode", this, node, index);
2632             }
2633             if(oldParent){
2634                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2635             }
2636             return node;
2637         }
2638     },
2639
2640     /**
2641      * Removes a child node from this node.
2642      * @param {Node} node The node to remove
2643      * @return {Node} The removed node
2644      */
2645     removeChild : function(node){
2646         var index = this.childNodes.indexOf(node);
2647         if(index == -1){
2648             return false;
2649         }
2650         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2651             return false;
2652         }
2653
2654         // remove it from childNodes collection
2655         this.childNodes.splice(index, 1);
2656
2657         // update siblings
2658         if(node.previousSibling){
2659             node.previousSibling.nextSibling = node.nextSibling;
2660         }
2661         if(node.nextSibling){
2662             node.nextSibling.previousSibling = node.previousSibling;
2663         }
2664
2665         // update child refs
2666         if(this.firstChild == node){
2667             this.setFirstChild(node.nextSibling);
2668         }
2669         if(this.lastChild == node){
2670             this.setLastChild(node.previousSibling);
2671         }
2672
2673         node.setOwnerTree(null);
2674         // clear any references from the node
2675         node.parentNode = null;
2676         node.previousSibling = null;
2677         node.nextSibling = null;
2678         this.fireEvent("remove", this.ownerTree, this, node);
2679         return node;
2680     },
2681
2682     /**
2683      * Inserts the first node before the second node in this nodes childNodes collection.
2684      * @param {Node} node The node to insert
2685      * @param {Node} refNode The node to insert before (if null the node is appended)
2686      * @return {Node} The inserted node
2687      */
2688     insertBefore : function(node, refNode){
2689         if(!refNode){ // like standard Dom, refNode can be null for append
2690             return this.appendChild(node);
2691         }
2692         // nothing to do
2693         if(node == refNode){
2694             return false;
2695         }
2696
2697         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2698             return false;
2699         }
2700         var index = this.childNodes.indexOf(refNode);
2701         var oldParent = node.parentNode;
2702         var refIndex = index;
2703
2704         // when moving internally, indexes will change after remove
2705         if(oldParent == this && this.childNodes.indexOf(node) < index){
2706             refIndex--;
2707         }
2708
2709         // it's a move, make sure we move it cleanly
2710         if(oldParent){
2711             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2712                 return false;
2713             }
2714             oldParent.removeChild(node);
2715         }
2716         if(refIndex == 0){
2717             this.setFirstChild(node);
2718         }
2719         this.childNodes.splice(refIndex, 0, node);
2720         node.parentNode = this;
2721         var ps = this.childNodes[refIndex-1];
2722         if(ps){
2723             node.previousSibling = ps;
2724             ps.nextSibling = node;
2725         }else{
2726             node.previousSibling = null;
2727         }
2728         node.nextSibling = refNode;
2729         refNode.previousSibling = node;
2730         node.setOwnerTree(this.getOwnerTree());
2731         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2732         if(oldParent){
2733             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2734         }
2735         return node;
2736     },
2737
2738     /**
2739      * Returns the child node at the specified index.
2740      * @param {Number} index
2741      * @return {Node}
2742      */
2743     item : function(index){
2744         return this.childNodes[index];
2745     },
2746
2747     /**
2748      * Replaces one child node in this node with another.
2749      * @param {Node} newChild The replacement node
2750      * @param {Node} oldChild The node to replace
2751      * @return {Node} The replaced node
2752      */
2753     replaceChild : function(newChild, oldChild){
2754         this.insertBefore(newChild, oldChild);
2755         this.removeChild(oldChild);
2756         return oldChild;
2757     },
2758
2759     /**
2760      * Returns the index of a child node
2761      * @param {Node} node
2762      * @return {Number} The index of the node or -1 if it was not found
2763      */
2764     indexOf : function(child){
2765         return this.childNodes.indexOf(child);
2766     },
2767
2768     /**
2769      * Returns the tree this node is in.
2770      * @return {Tree}
2771      */
2772     getOwnerTree : function(){
2773         // if it doesn't have one, look for one
2774         if(!this.ownerTree){
2775             var p = this;
2776             while(p){
2777                 if(p.ownerTree){
2778                     this.ownerTree = p.ownerTree;
2779                     break;
2780                 }
2781                 p = p.parentNode;
2782             }
2783         }
2784         return this.ownerTree;
2785     },
2786
2787     /**
2788      * Returns depth of this node (the root node has a depth of 0)
2789      * @return {Number}
2790      */
2791     getDepth : function(){
2792         var depth = 0;
2793         var p = this;
2794         while(p.parentNode){
2795             ++depth;
2796             p = p.parentNode;
2797         }
2798         return depth;
2799     },
2800
2801     // private
2802     setOwnerTree : function(tree){
2803         // if it's move, we need to update everyone
2804         if(tree != this.ownerTree){
2805             if(this.ownerTree){
2806                 this.ownerTree.unregisterNode(this);
2807             }
2808             this.ownerTree = tree;
2809             var cs = this.childNodes;
2810             for(var i = 0, len = cs.length; i < len; i++) {
2811                 cs[i].setOwnerTree(tree);
2812             }
2813             if(tree){
2814                 tree.registerNode(this);
2815             }
2816         }
2817     },
2818
2819     /**
2820      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2821      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2822      * @return {String} The path
2823      */
2824     getPath : function(attr){
2825         attr = attr || "id";
2826         var p = this.parentNode;
2827         var b = [this.attributes[attr]];
2828         while(p){
2829             b.unshift(p.attributes[attr]);
2830             p = p.parentNode;
2831         }
2832         var sep = this.getOwnerTree().pathSeparator;
2833         return sep + b.join(sep);
2834     },
2835
2836     /**
2837      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2838      * function call will be the scope provided or the current node. The arguments to the function
2839      * will be the args provided or the current node. If the function returns false at any point,
2840      * the bubble is stopped.
2841      * @param {Function} fn The function to call
2842      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2843      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2844      */
2845     bubble : function(fn, scope, args){
2846         var p = this;
2847         while(p){
2848             if(fn.call(scope || p, args || p) === false){
2849                 break;
2850             }
2851             p = p.parentNode;
2852         }
2853     },
2854
2855     /**
2856      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2857      * function call will be the scope provided or the current node. The arguments to the function
2858      * will be the args provided or the current node. If the function returns false at any point,
2859      * the cascade is stopped on that branch.
2860      * @param {Function} fn The function to call
2861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2863      */
2864     cascade : function(fn, scope, args){
2865         if(fn.call(scope || this, args || this) !== false){
2866             var cs = this.childNodes;
2867             for(var i = 0, len = cs.length; i < len; i++) {
2868                 cs[i].cascade(fn, scope, args);
2869             }
2870         }
2871     },
2872
2873     /**
2874      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2875      * function call will be the scope provided or the current node. The arguments to the function
2876      * will be the args provided or the current node. If the function returns false at any point,
2877      * the iteration stops.
2878      * @param {Function} fn The function to call
2879      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2880      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2881      */
2882     eachChild : function(fn, scope, args){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(fn.call(scope || this, args || cs[i]) === false){
2886                     break;
2887                 }
2888         }
2889     },
2890
2891     /**
2892      * Finds the first child that has the attribute with the specified value.
2893      * @param {String} attribute The attribute name
2894      * @param {Mixed} value The value to search for
2895      * @return {Node} The found child or null if none was found
2896      */
2897     findChild : function(attribute, value){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(cs[i].attributes[attribute] == value){
2901                     return cs[i];
2902                 }
2903         }
2904         return null;
2905     },
2906
2907     /**
2908      * Finds the first child by a custom function. The child matches if the function passed
2909      * returns true.
2910      * @param {Function} fn
2911      * @param {Object} scope (optional)
2912      * @return {Node} The found child or null if none was found
2913      */
2914     findChildBy : function(fn, scope){
2915         var cs = this.childNodes;
2916         for(var i = 0, len = cs.length; i < len; i++) {
2917                 if(fn.call(scope||cs[i], cs[i]) === true){
2918                     return cs[i];
2919                 }
2920         }
2921         return null;
2922     },
2923
2924     /**
2925      * Sorts this nodes children using the supplied sort function
2926      * @param {Function} fn
2927      * @param {Object} scope (optional)
2928      */
2929     sort : function(fn, scope){
2930         var cs = this.childNodes;
2931         var len = cs.length;
2932         if(len > 0){
2933             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2934             cs.sort(sortFn);
2935             for(var i = 0; i < len; i++){
2936                 var n = cs[i];
2937                 n.previousSibling = cs[i-1];
2938                 n.nextSibling = cs[i+1];
2939                 if(i == 0){
2940                     this.setFirstChild(n);
2941                 }
2942                 if(i == len-1){
2943                     this.setLastChild(n);
2944                 }
2945             }
2946         }
2947     },
2948
2949     /**
2950      * Returns true if this node is an ancestor (at any point) of the passed node.
2951      * @param {Node} node
2952      * @return {Boolean}
2953      */
2954     contains : function(node){
2955         return node.isAncestor(this);
2956     },
2957
2958     /**
2959      * Returns true if the passed node is an ancestor (at any point) of this node.
2960      * @param {Node} node
2961      * @return {Boolean}
2962      */
2963     isAncestor : function(node){
2964         var p = this.parentNode;
2965         while(p){
2966             if(p == node){
2967                 return true;
2968             }
2969             p = p.parentNode;
2970         }
2971         return false;
2972     },
2973
2974     toString : function(){
2975         return "[Node"+(this.id?" "+this.id:"")+"]";
2976     }
2977 });/*
2978  * Based on:
2979  * Ext JS Library 1.1.1
2980  * Copyright(c) 2006-2007, Ext JS, LLC.
2981  *
2982  * Originally Released Under LGPL - original licence link has changed is not relivant.
2983  *
2984  * Fork - LGPL
2985  * <script type="text/javascript">
2986  */
2987
2988
2989 /**
2990  * @class Roo.Shadow
2991  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2992  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2993  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2994  * @constructor
2995  * Create a new Shadow
2996  * @param {Object} config The config object
2997  */
2998 Roo.Shadow = function(config){
2999     Roo.apply(this, config);
3000     if(typeof this.mode != "string"){
3001         this.mode = this.defaultMode;
3002     }
3003     var o = this.offset, a = {h: 0};
3004     var rad = Math.floor(this.offset/2);
3005     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3006         case "drop":
3007             a.w = 0;
3008             a.l = a.t = o;
3009             a.t -= 1;
3010             if(Roo.isIE){
3011                 a.l -= this.offset + rad;
3012                 a.t -= this.offset + rad;
3013                 a.w -= rad;
3014                 a.h -= rad;
3015                 a.t += 1;
3016             }
3017         break;
3018         case "sides":
3019             a.w = (o*2);
3020             a.l = -o;
3021             a.t = o-1;
3022             if(Roo.isIE){
3023                 a.l -= (this.offset - rad);
3024                 a.t -= this.offset + rad;
3025                 a.l += 1;
3026                 a.w -= (this.offset - rad)*2;
3027                 a.w -= rad + 1;
3028                 a.h -= 1;
3029             }
3030         break;
3031         case "frame":
3032             a.w = a.h = (o*2);
3033             a.l = a.t = -o;
3034             a.t += 1;
3035             a.h -= 2;
3036             if(Roo.isIE){
3037                 a.l -= (this.offset - rad);
3038                 a.t -= (this.offset - rad);
3039                 a.l += 1;
3040                 a.w -= (this.offset + rad + 1);
3041                 a.h -= (this.offset + rad);
3042                 a.h += 1;
3043             }
3044         break;
3045     };
3046
3047     this.adjusts = a;
3048 };
3049
3050 Roo.Shadow.prototype = {
3051     /**
3052      * @cfg {String} mode
3053      * The shadow display mode.  Supports the following options:<br />
3054      * sides: Shadow displays on both sides and bottom only<br />
3055      * frame: Shadow displays equally on all four sides<br />
3056      * drop: Traditional bottom-right drop shadow (default)
3057      */
3058     mode: false,
3059     /**
3060      * @cfg {String} offset
3061      * The number of pixels to offset the shadow from the element (defaults to 4)
3062      */
3063     offset: 4,
3064
3065     // private
3066     defaultMode: "drop",
3067
3068     /**
3069      * Displays the shadow under the target element
3070      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3071      */
3072     show : function(target){
3073         target = Roo.get(target);
3074         if(!this.el){
3075             this.el = Roo.Shadow.Pool.pull();
3076             if(this.el.dom.nextSibling != target.dom){
3077                 this.el.insertBefore(target);
3078             }
3079         }
3080         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3081         if(Roo.isIE){
3082             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3083         }
3084         this.realign(
3085             target.getLeft(true),
3086             target.getTop(true),
3087             target.getWidth(),
3088             target.getHeight()
3089         );
3090         this.el.dom.style.display = "block";
3091     },
3092
3093     /**
3094      * Returns true if the shadow is visible, else false
3095      */
3096     isVisible : function(){
3097         return this.el ? true : false;  
3098     },
3099
3100     /**
3101      * Direct alignment when values are already available. Show must be called at least once before
3102      * calling this method to ensure it is initialized.
3103      * @param {Number} left The target element left position
3104      * @param {Number} top The target element top position
3105      * @param {Number} width The target element width
3106      * @param {Number} height The target element height
3107      */
3108     realign : function(l, t, w, h){
3109         if(!this.el){
3110             return;
3111         }
3112         var a = this.adjusts, d = this.el.dom, s = d.style;
3113         var iea = 0;
3114         s.left = (l+a.l)+"px";
3115         s.top = (t+a.t)+"px";
3116         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3117  
3118         if(s.width != sws || s.height != shs){
3119             s.width = sws;
3120             s.height = shs;
3121             if(!Roo.isIE){
3122                 var cn = d.childNodes;
3123                 var sww = Math.max(0, (sw-12))+"px";
3124                 cn[0].childNodes[1].style.width = sww;
3125                 cn[1].childNodes[1].style.width = sww;
3126                 cn[2].childNodes[1].style.width = sww;
3127                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3128             }
3129         }
3130     },
3131
3132     /**
3133      * Hides this shadow
3134      */
3135     hide : function(){
3136         if(this.el){
3137             this.el.dom.style.display = "none";
3138             Roo.Shadow.Pool.push(this.el);
3139             delete this.el;
3140         }
3141     },
3142
3143     /**
3144      * Adjust the z-index of this shadow
3145      * @param {Number} zindex The new z-index
3146      */
3147     setZIndex : function(z){
3148         this.zIndex = z;
3149         if(this.el){
3150             this.el.setStyle("z-index", z);
3151         }
3152     }
3153 };
3154
3155 // Private utility class that manages the internal Shadow cache
3156 Roo.Shadow.Pool = function(){
3157     var p = [];
3158     var markup = Roo.isIE ?
3159                  '<div class="x-ie-shadow"></div>' :
3160                  '<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>';
3161     return {
3162         pull : function(){
3163             var sh = p.shift();
3164             if(!sh){
3165                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3166                 sh.autoBoxAdjust = false;
3167             }
3168             return sh;
3169         },
3170
3171         push : function(sh){
3172             p.push(sh);
3173         }
3174     };
3175 }();/*
3176  * Based on:
3177  * Ext JS Library 1.1.1
3178  * Copyright(c) 2006-2007, Ext JS, LLC.
3179  *
3180  * Originally Released Under LGPL - original licence link has changed is not relivant.
3181  *
3182  * Fork - LGPL
3183  * <script type="text/javascript">
3184  */
3185
3186
3187 /**
3188  * @class Roo.SplitBar
3189  * @extends Roo.util.Observable
3190  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3191  * <br><br>
3192  * Usage:
3193  * <pre><code>
3194 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3195                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3196 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3197 split.minSize = 100;
3198 split.maxSize = 600;
3199 split.animate = true;
3200 split.on('moved', splitterMoved);
3201 </code></pre>
3202  * @constructor
3203  * Create a new SplitBar
3204  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3205  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3206  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3207  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3208                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3209                         position of the SplitBar).
3210  */
3211 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3212     
3213     /** @private */
3214     this.el = Roo.get(dragElement, true);
3215     this.el.dom.unselectable = "on";
3216     /** @private */
3217     this.resizingEl = Roo.get(resizingElement, true);
3218
3219     /**
3220      * @private
3221      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3223      * @type Number
3224      */
3225     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3226     
3227     /**
3228      * The minimum size of the resizing element. (Defaults to 0)
3229      * @type Number
3230      */
3231     this.minSize = 0;
3232     
3233     /**
3234      * The maximum size of the resizing element. (Defaults to 2000)
3235      * @type Number
3236      */
3237     this.maxSize = 2000;
3238     
3239     /**
3240      * Whether to animate the transition to the new size
3241      * @type Boolean
3242      */
3243     this.animate = false;
3244     
3245     /**
3246      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3247      * @type Boolean
3248      */
3249     this.useShim = false;
3250     
3251     /** @private */
3252     this.shim = null;
3253     
3254     if(!existingProxy){
3255         /** @private */
3256         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3257     }else{
3258         this.proxy = Roo.get(existingProxy).dom;
3259     }
3260     /** @private */
3261     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3262     
3263     /** @private */
3264     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3265     
3266     /** @private */
3267     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3268     
3269     /** @private */
3270     this.dragSpecs = {};
3271     
3272     /**
3273      * @private The adapter to use to positon and resize elements
3274      */
3275     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3276     this.adapter.init(this);
3277     
3278     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3279         /** @private */
3280         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3281         this.el.addClass("x-splitbar-h");
3282     }else{
3283         /** @private */
3284         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3285         this.el.addClass("x-splitbar-v");
3286     }
3287     
3288     this.addEvents({
3289         /**
3290          * @event resize
3291          * Fires when the splitter is moved (alias for {@link #event-moved})
3292          * @param {Roo.SplitBar} this
3293          * @param {Number} newSize the new width or height
3294          */
3295         "resize" : true,
3296         /**
3297          * @event moved
3298          * Fires when the splitter is moved
3299          * @param {Roo.SplitBar} this
3300          * @param {Number} newSize the new width or height
3301          */
3302         "moved" : true,
3303         /**
3304          * @event beforeresize
3305          * Fires before the splitter is dragged
3306          * @param {Roo.SplitBar} this
3307          */
3308         "beforeresize" : true,
3309
3310         "beforeapply" : true
3311     });
3312
3313     Roo.util.Observable.call(this);
3314 };
3315
3316 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3317     onStartProxyDrag : function(x, y){
3318         this.fireEvent("beforeresize", this);
3319         if(!this.overlay){
3320             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3321             o.unselectable();
3322             o.enableDisplayMode("block");
3323             // all splitbars share the same overlay
3324             Roo.SplitBar.prototype.overlay = o;
3325         }
3326         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3327         this.overlay.show();
3328         Roo.get(this.proxy).setDisplayed("block");
3329         var size = this.adapter.getElementSize(this);
3330         this.activeMinSize = this.getMinimumSize();;
3331         this.activeMaxSize = this.getMaximumSize();;
3332         var c1 = size - this.activeMinSize;
3333         var c2 = Math.max(this.activeMaxSize - size, 0);
3334         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3335             this.dd.resetConstraints();
3336             this.dd.setXConstraint(
3337                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3338                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3339             );
3340             this.dd.setYConstraint(0, 0);
3341         }else{
3342             this.dd.resetConstraints();
3343             this.dd.setXConstraint(0, 0);
3344             this.dd.setYConstraint(
3345                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3346                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3347             );
3348          }
3349         this.dragSpecs.startSize = size;
3350         this.dragSpecs.startPoint = [x, y];
3351         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3352     },
3353     
3354     /** 
3355      * @private Called after the drag operation by the DDProxy
3356      */
3357     onEndProxyDrag : function(e){
3358         Roo.get(this.proxy).setDisplayed(false);
3359         var endPoint = Roo.lib.Event.getXY(e);
3360         if(this.overlay){
3361             this.overlay.hide();
3362         }
3363         var newSize;
3364         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3365             newSize = this.dragSpecs.startSize + 
3366                 (this.placement == Roo.SplitBar.LEFT ?
3367                     endPoint[0] - this.dragSpecs.startPoint[0] :
3368                     this.dragSpecs.startPoint[0] - endPoint[0]
3369                 );
3370         }else{
3371             newSize = this.dragSpecs.startSize + 
3372                 (this.placement == Roo.SplitBar.TOP ?
3373                     endPoint[1] - this.dragSpecs.startPoint[1] :
3374                     this.dragSpecs.startPoint[1] - endPoint[1]
3375                 );
3376         }
3377         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3378         if(newSize != this.dragSpecs.startSize){
3379             if(this.fireEvent('beforeapply', this, newSize) !== false){
3380                 this.adapter.setElementSize(this, newSize);
3381                 this.fireEvent("moved", this, newSize);
3382                 this.fireEvent("resize", this, newSize);
3383             }
3384         }
3385     },
3386     
3387     /**
3388      * Get the adapter this SplitBar uses
3389      * @return The adapter object
3390      */
3391     getAdapter : function(){
3392         return this.adapter;
3393     },
3394     
3395     /**
3396      * Set the adapter this SplitBar uses
3397      * @param {Object} adapter A SplitBar adapter object
3398      */
3399     setAdapter : function(adapter){
3400         this.adapter = adapter;
3401         this.adapter.init(this);
3402     },
3403     
3404     /**
3405      * Gets the minimum size for the resizing element
3406      * @return {Number} The minimum size
3407      */
3408     getMinimumSize : function(){
3409         return this.minSize;
3410     },
3411     
3412     /**
3413      * Sets the minimum size for the resizing element
3414      * @param {Number} minSize The minimum size
3415      */
3416     setMinimumSize : function(minSize){
3417         this.minSize = minSize;
3418     },
3419     
3420     /**
3421      * Gets the maximum size for the resizing element
3422      * @return {Number} The maximum size
3423      */
3424     getMaximumSize : function(){
3425         return this.maxSize;
3426     },
3427     
3428     /**
3429      * Sets the maximum size for the resizing element
3430      * @param {Number} maxSize The maximum size
3431      */
3432     setMaximumSize : function(maxSize){
3433         this.maxSize = maxSize;
3434     },
3435     
3436     /**
3437      * Sets the initialize size for the resizing element
3438      * @param {Number} size The initial size
3439      */
3440     setCurrentSize : function(size){
3441         var oldAnimate = this.animate;
3442         this.animate = false;
3443         this.adapter.setElementSize(this, size);
3444         this.animate = oldAnimate;
3445     },
3446     
3447     /**
3448      * Destroy this splitbar. 
3449      * @param {Boolean} removeEl True to remove the element
3450      */
3451     destroy : function(removeEl){
3452         if(this.shim){
3453             this.shim.remove();
3454         }
3455         this.dd.unreg();
3456         this.proxy.parentNode.removeChild(this.proxy);
3457         if(removeEl){
3458             this.el.remove();
3459         }
3460     }
3461 });
3462
3463 /**
3464  * @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.
3465  */
3466 Roo.SplitBar.createProxy = function(dir){
3467     var proxy = new Roo.Element(document.createElement("div"));
3468     proxy.unselectable();
3469     var cls = 'x-splitbar-proxy';
3470     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3471     document.body.appendChild(proxy.dom);
3472     return proxy.dom;
3473 };
3474
3475 /** 
3476  * @class Roo.SplitBar.BasicLayoutAdapter
3477  * Default Adapter. It assumes the splitter and resizing element are not positioned
3478  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3479  */
3480 Roo.SplitBar.BasicLayoutAdapter = function(){
3481 };
3482
3483 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3484     // do nothing for now
3485     init : function(s){
3486     
3487     },
3488     /**
3489      * Called before drag operations to get the current size of the resizing element. 
3490      * @param {Roo.SplitBar} s The SplitBar using this adapter
3491      */
3492      getElementSize : function(s){
3493         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3494             return s.resizingEl.getWidth();
3495         }else{
3496             return s.resizingEl.getHeight();
3497         }
3498     },
3499     
3500     /**
3501      * Called after drag operations to set the size of the resizing element.
3502      * @param {Roo.SplitBar} s The SplitBar using this adapter
3503      * @param {Number} newSize The new size to set
3504      * @param {Function} onComplete A function to be invoked when resizing is complete
3505      */
3506     setElementSize : function(s, newSize, onComplete){
3507         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3508             if(!s.animate){
3509                 s.resizingEl.setWidth(newSize);
3510                 if(onComplete){
3511                     onComplete(s, newSize);
3512                 }
3513             }else{
3514                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3515             }
3516         }else{
3517             
3518             if(!s.animate){
3519                 s.resizingEl.setHeight(newSize);
3520                 if(onComplete){
3521                     onComplete(s, newSize);
3522                 }
3523             }else{
3524                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3525             }
3526         }
3527     }
3528 };
3529
3530 /** 
3531  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3532  * @extends Roo.SplitBar.BasicLayoutAdapter
3533  * Adapter that  moves the splitter element to align with the resized sizing element. 
3534  * Used with an absolute positioned SplitBar.
3535  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3536  * document.body, make sure you assign an id to the body element.
3537  */
3538 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3539     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3540     this.container = Roo.get(container);
3541 };
3542
3543 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3544     init : function(s){
3545         this.basic.init(s);
3546     },
3547     
3548     getElementSize : function(s){
3549         return this.basic.getElementSize(s);
3550     },
3551     
3552     setElementSize : function(s, newSize, onComplete){
3553         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3554     },
3555     
3556     moveSplitter : function(s){
3557         var yes = Roo.SplitBar;
3558         switch(s.placement){
3559             case yes.LEFT:
3560                 s.el.setX(s.resizingEl.getRight());
3561                 break;
3562             case yes.RIGHT:
3563                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3564                 break;
3565             case yes.TOP:
3566                 s.el.setY(s.resizingEl.getBottom());
3567                 break;
3568             case yes.BOTTOM:
3569                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3570                 break;
3571         }
3572     }
3573 };
3574
3575 /**
3576  * Orientation constant - Create a vertical SplitBar
3577  * @static
3578  * @type Number
3579  */
3580 Roo.SplitBar.VERTICAL = 1;
3581
3582 /**
3583  * Orientation constant - Create a horizontal SplitBar
3584  * @static
3585  * @type Number
3586  */
3587 Roo.SplitBar.HORIZONTAL = 2;
3588
3589 /**
3590  * Placement constant - The resizing element is to the left of the splitter element
3591  * @static
3592  * @type Number
3593  */
3594 Roo.SplitBar.LEFT = 1;
3595
3596 /**
3597  * Placement constant - The resizing element is to the right of the splitter element
3598  * @static
3599  * @type Number
3600  */
3601 Roo.SplitBar.RIGHT = 2;
3602
3603 /**
3604  * Placement constant - The resizing element is positioned above the splitter element
3605  * @static
3606  * @type Number
3607  */
3608 Roo.SplitBar.TOP = 3;
3609
3610 /**
3611  * Placement constant - The resizing element is positioned under splitter element
3612  * @static
3613  * @type Number
3614  */
3615 Roo.SplitBar.BOTTOM = 4;
3616 /*
3617  * Based on:
3618  * Ext JS Library 1.1.1
3619  * Copyright(c) 2006-2007, Ext JS, LLC.
3620  *
3621  * Originally Released Under LGPL - original licence link has changed is not relivant.
3622  *
3623  * Fork - LGPL
3624  * <script type="text/javascript">
3625  */
3626
3627 /**
3628  * @class Roo.View
3629  * @extends Roo.util.Observable
3630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3631  * This class also supports single and multi selection modes. <br>
3632  * Create a data model bound view:
3633  <pre><code>
3634  var store = new Roo.data.Store(...);
3635
3636  var view = new Roo.View({
3637     el : "my-element",
3638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3639  
3640     singleSelect: true,
3641     selectedClass: "ydataview-selected",
3642     store: store
3643  });
3644
3645  // listen for node click?
3646  view.on("click", function(vw, index, node, e){
3647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3648  });
3649
3650  // load XML data
3651  dataModel.load("foobar.xml");
3652  </code></pre>
3653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3654  * <br><br>
3655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3657  * 
3658  * Note: old style constructor is still suported (container, template, config)
3659  * 
3660  * @constructor
3661  * Create a new View
3662  * @param {Object} config The config object
3663  * 
3664  */
3665 Roo.View = function(config, depreciated_tpl, depreciated_config){
3666     
3667     this.parent = false;
3668     
3669     if (typeof(depreciated_tpl) == 'undefined') {
3670         // new way.. - universal constructor.
3671         Roo.apply(this, config);
3672         this.el  = Roo.get(this.el);
3673     } else {
3674         // old format..
3675         this.el  = Roo.get(config);
3676         this.tpl = depreciated_tpl;
3677         Roo.apply(this, depreciated_config);
3678     }
3679     this.wrapEl  = this.el.wrap().wrap();
3680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3681     
3682     
3683     if(typeof(this.tpl) == "string"){
3684         this.tpl = new Roo.Template(this.tpl);
3685     } else {
3686         // support xtype ctors..
3687         this.tpl = new Roo.factory(this.tpl, Roo);
3688     }
3689     
3690     
3691     this.tpl.compile();
3692     
3693     /** @private */
3694     this.addEvents({
3695         /**
3696          * @event beforeclick
3697          * Fires before a click is processed. Returns false to cancel the default action.
3698          * @param {Roo.View} this
3699          * @param {Number} index The index of the target node
3700          * @param {HTMLElement} node The target node
3701          * @param {Roo.EventObject} e The raw event object
3702          */
3703             "beforeclick" : true,
3704         /**
3705          * @event click
3706          * Fires when a template node is clicked.
3707          * @param {Roo.View} this
3708          * @param {Number} index The index of the target node
3709          * @param {HTMLElement} node The target node
3710          * @param {Roo.EventObject} e The raw event object
3711          */
3712             "click" : true,
3713         /**
3714          * @event dblclick
3715          * Fires when a template node is double clicked.
3716          * @param {Roo.View} this
3717          * @param {Number} index The index of the target node
3718          * @param {HTMLElement} node The target node
3719          * @param {Roo.EventObject} e The raw event object
3720          */
3721             "dblclick" : true,
3722         /**
3723          * @event contextmenu
3724          * Fires when a template node is right clicked.
3725          * @param {Roo.View} this
3726          * @param {Number} index The index of the target node
3727          * @param {HTMLElement} node The target node
3728          * @param {Roo.EventObject} e The raw event object
3729          */
3730             "contextmenu" : true,
3731         /**
3732          * @event selectionchange
3733          * Fires when the selected nodes change.
3734          * @param {Roo.View} this
3735          * @param {Array} selections Array of the selected nodes
3736          */
3737             "selectionchange" : true,
3738     
3739         /**
3740          * @event beforeselect
3741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3742          * @param {Roo.View} this
3743          * @param {HTMLElement} node The node to be selected
3744          * @param {Array} selections Array of currently selected nodes
3745          */
3746             "beforeselect" : true,
3747         /**
3748          * @event preparedata
3749          * Fires on every row to render, to allow you to change the data.
3750          * @param {Roo.View} this
3751          * @param {Object} data to be rendered (change this)
3752          */
3753           "preparedata" : true
3754           
3755           
3756         });
3757
3758
3759
3760     this.el.on({
3761         "click": this.onClick,
3762         "dblclick": this.onDblClick,
3763         "contextmenu": this.onContextMenu,
3764         scope:this
3765     });
3766
3767     this.selections = [];
3768     this.nodes = [];
3769     this.cmp = new Roo.CompositeElementLite([]);
3770     if(this.store){
3771         this.store = Roo.factory(this.store, Roo.data);
3772         this.setStore(this.store, true);
3773     }
3774     
3775     if ( this.footer && this.footer.xtype) {
3776            
3777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3778         
3779         this.footer.dataSource = this.store;
3780         this.footer.container = fctr;
3781         this.footer = Roo.factory(this.footer, Roo);
3782         fctr.insertFirst(this.el);
3783         
3784         // this is a bit insane - as the paging toolbar seems to detach the el..
3785 //        dom.parentNode.parentNode.parentNode
3786          // they get detached?
3787     }
3788     
3789     
3790     Roo.View.superclass.constructor.call(this);
3791     
3792     
3793 };
3794
3795 Roo.extend(Roo.View, Roo.util.Observable, {
3796     
3797      /**
3798      * @cfg {Roo.data.Store} store Data store to load data from.
3799      */
3800     store : false,
3801     
3802     /**
3803      * @cfg {String|Roo.Element} el The container element.
3804      */
3805     el : '',
3806     
3807     /**
3808      * @cfg {String|Roo.Template} tpl The template used by this View 
3809      */
3810     tpl : false,
3811     /**
3812      * @cfg {String} dataName the named area of the template to use as the data area
3813      *                          Works with domtemplates roo-name="name"
3814      */
3815     dataName: false,
3816     /**
3817      * @cfg {String} selectedClass The css class to add to selected nodes
3818      */
3819     selectedClass : "x-view-selected",
3820      /**
3821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3822      */
3823     emptyText : "",
3824     
3825     /**
3826      * @cfg {String} text to display on mask (default Loading)
3827      */
3828     mask : false,
3829     /**
3830      * @cfg {Boolean} multiSelect Allow multiple selection
3831      */
3832     multiSelect : false,
3833     /**
3834      * @cfg {Boolean} singleSelect Allow single selection
3835      */
3836     singleSelect:  false,
3837     
3838     /**
3839      * @cfg {Boolean} toggleSelect - selecting 
3840      */
3841     toggleSelect : false,
3842     
3843     /**
3844      * @cfg {Boolean} tickable - selecting 
3845      */
3846     tickable : false,
3847     
3848     /**
3849      * Returns the element this view is bound to.
3850      * @return {Roo.Element}
3851      */
3852     getEl : function(){
3853         return this.wrapEl;
3854     },
3855     
3856     
3857
3858     /**
3859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3860      */
3861     refresh : function(){
3862         //Roo.log('refresh');
3863         var t = this.tpl;
3864         
3865         // if we are using something like 'domtemplate', then
3866         // the what gets used is:
3867         // t.applySubtemplate(NAME, data, wrapping data..)
3868         // the outer template then get' applied with
3869         //     the store 'extra data'
3870         // and the body get's added to the
3871         //      roo-name="data" node?
3872         //      <span class='roo-tpl-{name}'></span> ?????
3873         
3874         
3875         
3876         this.clearSelections();
3877         this.el.update("");
3878         var html = [];
3879         var records = this.store.getRange();
3880         if(records.length < 1) {
3881             
3882             // is this valid??  = should it render a template??
3883             
3884             this.el.update(this.emptyText);
3885             return;
3886         }
3887         var el = this.el;
3888         if (this.dataName) {
3889             this.el.update(t.apply(this.store.meta)); //????
3890             el = this.el.child('.roo-tpl-' + this.dataName);
3891         }
3892         
3893         for(var i = 0, len = records.length; i < len; i++){
3894             var data = this.prepareData(records[i].data, i, records[i]);
3895             this.fireEvent("preparedata", this, data, i, records[i]);
3896             
3897             var d = Roo.apply({}, data);
3898             
3899             if(this.tickable){
3900                 Roo.apply(d, {'roo-id' : Roo.id()});
3901                 
3902                 var _this = this;
3903             
3904                 Roo.each(this.parent.item, function(item){
3905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3906                         return;
3907                     }
3908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3909                 });
3910             }
3911             
3912             html[html.length] = Roo.util.Format.trim(
3913                 this.dataName ?
3914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3915                     t.apply(d)
3916             );
3917         }
3918         
3919         
3920         
3921         el.update(html.join(""));
3922         this.nodes = el.dom.childNodes;
3923         this.updateIndexes(0);
3924     },
3925     
3926
3927     /**
3928      * Function to override to reformat the data that is sent to
3929      * the template for each node.
3930      * DEPRICATED - use the preparedata event handler.
3931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3932      * a JSON object for an UpdateManager bound view).
3933      */
3934     prepareData : function(data, index, record)
3935     {
3936         this.fireEvent("preparedata", this, data, index, record);
3937         return data;
3938     },
3939
3940     onUpdate : function(ds, record){
3941         // Roo.log('on update');   
3942         this.clearSelections();
3943         var index = this.store.indexOf(record);
3944         var n = this.nodes[index];
3945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3946         n.parentNode.removeChild(n);
3947         this.updateIndexes(index, index);
3948     },
3949
3950     
3951     
3952 // --------- FIXME     
3953     onAdd : function(ds, records, index)
3954     {
3955         //Roo.log(['on Add', ds, records, index] );        
3956         this.clearSelections();
3957         if(this.nodes.length == 0){
3958             this.refresh();
3959             return;
3960         }
3961         var n = this.nodes[index];
3962         for(var i = 0, len = records.length; i < len; i++){
3963             var d = this.prepareData(records[i].data, i, records[i]);
3964             if(n){
3965                 this.tpl.insertBefore(n, d);
3966             }else{
3967                 
3968                 this.tpl.append(this.el, d);
3969             }
3970         }
3971         this.updateIndexes(index);
3972     },
3973
3974     onRemove : function(ds, record, index){
3975        // Roo.log('onRemove');
3976         this.clearSelections();
3977         var el = this.dataName  ?
3978             this.el.child('.roo-tpl-' + this.dataName) :
3979             this.el; 
3980         
3981         el.dom.removeChild(this.nodes[index]);
3982         this.updateIndexes(index);
3983     },
3984
3985     /**
3986      * Refresh an individual node.
3987      * @param {Number} index
3988      */
3989     refreshNode : function(index){
3990         this.onUpdate(this.store, this.store.getAt(index));
3991     },
3992
3993     updateIndexes : function(startIndex, endIndex){
3994         var ns = this.nodes;
3995         startIndex = startIndex || 0;
3996         endIndex = endIndex || ns.length - 1;
3997         for(var i = startIndex; i <= endIndex; i++){
3998             ns[i].nodeIndex = i;
3999         }
4000     },
4001
4002     /**
4003      * Changes the data store this view uses and refresh the view.
4004      * @param {Store} store
4005      */
4006     setStore : function(store, initial){
4007         if(!initial && this.store){
4008             this.store.un("datachanged", this.refresh);
4009             this.store.un("add", this.onAdd);
4010             this.store.un("remove", this.onRemove);
4011             this.store.un("update", this.onUpdate);
4012             this.store.un("clear", this.refresh);
4013             this.store.un("beforeload", this.onBeforeLoad);
4014             this.store.un("load", this.onLoad);
4015             this.store.un("loadexception", this.onLoad);
4016         }
4017         if(store){
4018           
4019             store.on("datachanged", this.refresh, this);
4020             store.on("add", this.onAdd, this);
4021             store.on("remove", this.onRemove, this);
4022             store.on("update", this.onUpdate, this);
4023             store.on("clear", this.refresh, this);
4024             store.on("beforeload", this.onBeforeLoad, this);
4025             store.on("load", this.onLoad, this);
4026             store.on("loadexception", this.onLoad, this);
4027         }
4028         
4029         if(store){
4030             this.refresh();
4031         }
4032     },
4033     /**
4034      * onbeforeLoad - masks the loading area.
4035      *
4036      */
4037     onBeforeLoad : function(store,opts)
4038     {
4039          //Roo.log('onBeforeLoad');   
4040         if (!opts.add) {
4041             this.el.update("");
4042         }
4043         this.el.mask(this.mask ? this.mask : "Loading" ); 
4044     },
4045     onLoad : function ()
4046     {
4047         this.el.unmask();
4048     },
4049     
4050
4051     /**
4052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4053      * @param {HTMLElement} node
4054      * @return {HTMLElement} The template node
4055      */
4056     findItemFromChild : function(node){
4057         var el = this.dataName  ?
4058             this.el.child('.roo-tpl-' + this.dataName,true) :
4059             this.el.dom; 
4060         
4061         if(!node || node.parentNode == el){
4062                     return node;
4063             }
4064             var p = node.parentNode;
4065             while(p && p != el){
4066             if(p.parentNode == el){
4067                 return p;
4068             }
4069             p = p.parentNode;
4070         }
4071             return null;
4072     },
4073
4074     /** @ignore */
4075     onClick : function(e){
4076         var item = this.findItemFromChild(e.getTarget());
4077         if(item){
4078             var index = this.indexOf(item);
4079             if(this.onItemClick(item, index, e) !== false){
4080                 this.fireEvent("click", this, index, item, e);
4081             }
4082         }else{
4083             this.clearSelections();
4084         }
4085     },
4086
4087     /** @ignore */
4088     onContextMenu : function(e){
4089         var item = this.findItemFromChild(e.getTarget());
4090         if(item){
4091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4092         }
4093     },
4094
4095     /** @ignore */
4096     onDblClick : function(e){
4097         var item = this.findItemFromChild(e.getTarget());
4098         if(item){
4099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4100         }
4101     },
4102
4103     onItemClick : function(item, index, e)
4104     {
4105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4106             return false;
4107         }
4108         if (this.toggleSelect) {
4109             var m = this.isSelected(item) ? 'unselect' : 'select';
4110             //Roo.log(m);
4111             var _t = this;
4112             _t[m](item, true, false);
4113             return true;
4114         }
4115         if(this.multiSelect || this.singleSelect){
4116             if(this.multiSelect && e.shiftKey && this.lastSelection){
4117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4118             }else{
4119                 this.select(item, this.multiSelect && e.ctrlKey);
4120                 this.lastSelection = item;
4121             }
4122             
4123             if(!this.tickable){
4124                 e.preventDefault();
4125             }
4126             
4127         }
4128         return true;
4129     },
4130
4131     /**
4132      * Get the number of selected nodes.
4133      * @return {Number}
4134      */
4135     getSelectionCount : function(){
4136         return this.selections.length;
4137     },
4138
4139     /**
4140      * Get the currently selected nodes.
4141      * @return {Array} An array of HTMLElements
4142      */
4143     getSelectedNodes : function(){
4144         return this.selections;
4145     },
4146
4147     /**
4148      * Get the indexes of the selected nodes.
4149      * @return {Array}
4150      */
4151     getSelectedIndexes : function(){
4152         var indexes = [], s = this.selections;
4153         for(var i = 0, len = s.length; i < len; i++){
4154             indexes.push(s[i].nodeIndex);
4155         }
4156         return indexes;
4157     },
4158
4159     /**
4160      * Clear all selections
4161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4162      */
4163     clearSelections : function(suppressEvent){
4164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4165             this.cmp.elements = this.selections;
4166             this.cmp.removeClass(this.selectedClass);
4167             this.selections = [];
4168             if(!suppressEvent){
4169                 this.fireEvent("selectionchange", this, this.selections);
4170             }
4171         }
4172     },
4173
4174     /**
4175      * Returns true if the passed node is selected
4176      * @param {HTMLElement/Number} node The node or node index
4177      * @return {Boolean}
4178      */
4179     isSelected : function(node){
4180         var s = this.selections;
4181         if(s.length < 1){
4182             return false;
4183         }
4184         node = this.getNode(node);
4185         return s.indexOf(node) !== -1;
4186     },
4187
4188     /**
4189      * Selects nodes.
4190      * @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
4191      * @param {Boolean} keepExisting (optional) true to keep existing selections
4192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4193      */
4194     select : function(nodeInfo, keepExisting, suppressEvent){
4195         if(nodeInfo instanceof Array){
4196             if(!keepExisting){
4197                 this.clearSelections(true);
4198             }
4199             for(var i = 0, len = nodeInfo.length; i < len; i++){
4200                 this.select(nodeInfo[i], true, true);
4201             }
4202             return;
4203         } 
4204         var node = this.getNode(nodeInfo);
4205         if(!node || this.isSelected(node)){
4206             return; // already selected.
4207         }
4208         if(!keepExisting){
4209             this.clearSelections(true);
4210         }
4211         
4212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4213             Roo.fly(node).addClass(this.selectedClass);
4214             this.selections.push(node);
4215             if(!suppressEvent){
4216                 this.fireEvent("selectionchange", this, this.selections);
4217             }
4218         }
4219         
4220         
4221     },
4222       /**
4223      * Unselects nodes.
4224      * @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
4225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4227      */
4228     unselect : function(nodeInfo, keepExisting, suppressEvent)
4229     {
4230         if(nodeInfo instanceof Array){
4231             Roo.each(this.selections, function(s) {
4232                 this.unselect(s, nodeInfo);
4233             }, this);
4234             return;
4235         }
4236         var node = this.getNode(nodeInfo);
4237         if(!node || !this.isSelected(node)){
4238             //Roo.log("not selected");
4239             return; // not selected.
4240         }
4241         // fireevent???
4242         var ns = [];
4243         Roo.each(this.selections, function(s) {
4244             if (s == node ) {
4245                 Roo.fly(node).removeClass(this.selectedClass);
4246
4247                 return;
4248             }
4249             ns.push(s);
4250         },this);
4251         
4252         this.selections= ns;
4253         this.fireEvent("selectionchange", this, this.selections);
4254     },
4255
4256     /**
4257      * Gets a template node.
4258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4259      * @return {HTMLElement} The node or null if it wasn't found
4260      */
4261     getNode : function(nodeInfo){
4262         if(typeof nodeInfo == "string"){
4263             return document.getElementById(nodeInfo);
4264         }else if(typeof nodeInfo == "number"){
4265             return this.nodes[nodeInfo];
4266         }
4267         return nodeInfo;
4268     },
4269
4270     /**
4271      * Gets a range template nodes.
4272      * @param {Number} startIndex
4273      * @param {Number} endIndex
4274      * @return {Array} An array of nodes
4275      */
4276     getNodes : function(start, end){
4277         var ns = this.nodes;
4278         start = start || 0;
4279         end = typeof end == "undefined" ? ns.length - 1 : end;
4280         var nodes = [];
4281         if(start <= end){
4282             for(var i = start; i <= end; i++){
4283                 nodes.push(ns[i]);
4284             }
4285         } else{
4286             for(var i = start; i >= end; i--){
4287                 nodes.push(ns[i]);
4288             }
4289         }
4290         return nodes;
4291     },
4292
4293     /**
4294      * Finds the index of the passed node
4295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4296      * @return {Number} The index of the node or -1
4297      */
4298     indexOf : function(node){
4299         node = this.getNode(node);
4300         if(typeof node.nodeIndex == "number"){
4301             return node.nodeIndex;
4302         }
4303         var ns = this.nodes;
4304         for(var i = 0, len = ns.length; i < len; i++){
4305             if(ns[i] == node){
4306                 return i;
4307             }
4308         }
4309         return -1;
4310     }
4311 });
4312 /*
4313  * Based on:
4314  * Ext JS Library 1.1.1
4315  * Copyright(c) 2006-2007, Ext JS, LLC.
4316  *
4317  * Originally Released Under LGPL - original licence link has changed is not relivant.
4318  *
4319  * Fork - LGPL
4320  * <script type="text/javascript">
4321  */
4322
4323 /**
4324  * @class Roo.JsonView
4325  * @extends Roo.View
4326  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4327 <pre><code>
4328 var view = new Roo.JsonView({
4329     container: "my-element",
4330     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4331     multiSelect: true, 
4332     jsonRoot: "data" 
4333 });
4334
4335 // listen for node click?
4336 view.on("click", function(vw, index, node, e){
4337     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4338 });
4339
4340 // direct load of JSON data
4341 view.load("foobar.php");
4342
4343 // Example from my blog list
4344 var tpl = new Roo.Template(
4345     '&lt;div class="entry"&gt;' +
4346     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4347     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4348     "&lt;/div&gt;&lt;hr /&gt;"
4349 );
4350
4351 var moreView = new Roo.JsonView({
4352     container :  "entry-list", 
4353     template : tpl,
4354     jsonRoot: "posts"
4355 });
4356 moreView.on("beforerender", this.sortEntries, this);
4357 moreView.load({
4358     url: "/blog/get-posts.php",
4359     params: "allposts=true",
4360     text: "Loading Blog Entries..."
4361 });
4362 </code></pre>
4363
4364 * Note: old code is supported with arguments : (container, template, config)
4365
4366
4367  * @constructor
4368  * Create a new JsonView
4369  * 
4370  * @param {Object} config The config object
4371  * 
4372  */
4373 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4374     
4375     
4376     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4377
4378     var um = this.el.getUpdateManager();
4379     um.setRenderer(this);
4380     um.on("update", this.onLoad, this);
4381     um.on("failure", this.onLoadException, this);
4382
4383     /**
4384      * @event beforerender
4385      * Fires before rendering of the downloaded JSON data.
4386      * @param {Roo.JsonView} this
4387      * @param {Object} data The JSON data loaded
4388      */
4389     /**
4390      * @event load
4391      * Fires when data is loaded.
4392      * @param {Roo.JsonView} this
4393      * @param {Object} data The JSON data loaded
4394      * @param {Object} response The raw Connect response object
4395      */
4396     /**
4397      * @event loadexception
4398      * Fires when loading fails.
4399      * @param {Roo.JsonView} this
4400      * @param {Object} response The raw Connect response object
4401      */
4402     this.addEvents({
4403         'beforerender' : true,
4404         'load' : true,
4405         'loadexception' : true
4406     });
4407 };
4408 Roo.extend(Roo.JsonView, Roo.View, {
4409     /**
4410      * @type {String} The root property in the loaded JSON object that contains the data
4411      */
4412     jsonRoot : "",
4413
4414     /**
4415      * Refreshes the view.
4416      */
4417     refresh : function(){
4418         this.clearSelections();
4419         this.el.update("");
4420         var html = [];
4421         var o = this.jsonData;
4422         if(o && o.length > 0){
4423             for(var i = 0, len = o.length; i < len; i++){
4424                 var data = this.prepareData(o[i], i, o);
4425                 html[html.length] = this.tpl.apply(data);
4426             }
4427         }else{
4428             html.push(this.emptyText);
4429         }
4430         this.el.update(html.join(""));
4431         this.nodes = this.el.dom.childNodes;
4432         this.updateIndexes(0);
4433     },
4434
4435     /**
4436      * 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.
4437      * @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:
4438      <pre><code>
4439      view.load({
4440          url: "your-url.php",
4441          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4442          callback: yourFunction,
4443          scope: yourObject, //(optional scope)
4444          discardUrl: false,
4445          nocache: false,
4446          text: "Loading...",
4447          timeout: 30,
4448          scripts: false
4449      });
4450      </code></pre>
4451      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4452      * 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.
4453      * @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}
4454      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4455      * @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.
4456      */
4457     load : function(){
4458         var um = this.el.getUpdateManager();
4459         um.update.apply(um, arguments);
4460     },
4461
4462     // note - render is a standard framework call...
4463     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4464     render : function(el, response){
4465         
4466         this.clearSelections();
4467         this.el.update("");
4468         var o;
4469         try{
4470             if (response != '') {
4471                 o = Roo.util.JSON.decode(response.responseText);
4472                 if(this.jsonRoot){
4473                     
4474                     o = o[this.jsonRoot];
4475                 }
4476             }
4477         } catch(e){
4478         }
4479         /**
4480          * The current JSON data or null
4481          */
4482         this.jsonData = o;
4483         this.beforeRender();
4484         this.refresh();
4485     },
4486
4487 /**
4488  * Get the number of records in the current JSON dataset
4489  * @return {Number}
4490  */
4491     getCount : function(){
4492         return this.jsonData ? this.jsonData.length : 0;
4493     },
4494
4495 /**
4496  * Returns the JSON object for the specified node(s)
4497  * @param {HTMLElement/Array} node The node or an array of nodes
4498  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4499  * you get the JSON object for the node
4500  */
4501     getNodeData : function(node){
4502         if(node instanceof Array){
4503             var data = [];
4504             for(var i = 0, len = node.length; i < len; i++){
4505                 data.push(this.getNodeData(node[i]));
4506             }
4507             return data;
4508         }
4509         return this.jsonData[this.indexOf(node)] || null;
4510     },
4511
4512     beforeRender : function(){
4513         this.snapshot = this.jsonData;
4514         if(this.sortInfo){
4515             this.sort.apply(this, this.sortInfo);
4516         }
4517         this.fireEvent("beforerender", this, this.jsonData);
4518     },
4519
4520     onLoad : function(el, o){
4521         this.fireEvent("load", this, this.jsonData, o);
4522     },
4523
4524     onLoadException : function(el, o){
4525         this.fireEvent("loadexception", this, o);
4526     },
4527
4528 /**
4529  * Filter the data by a specific property.
4530  * @param {String} property A property on your JSON objects
4531  * @param {String/RegExp} value Either string that the property values
4532  * should start with, or a RegExp to test against the property
4533  */
4534     filter : function(property, value){
4535         if(this.jsonData){
4536             var data = [];
4537             var ss = this.snapshot;
4538             if(typeof value == "string"){
4539                 var vlen = value.length;
4540                 if(vlen == 0){
4541                     this.clearFilter();
4542                     return;
4543                 }
4544                 value = value.toLowerCase();
4545                 for(var i = 0, len = ss.length; i < len; i++){
4546                     var o = ss[i];
4547                     if(o[property].substr(0, vlen).toLowerCase() == value){
4548                         data.push(o);
4549                     }
4550                 }
4551             } else if(value.exec){ // regex?
4552                 for(var i = 0, len = ss.length; i < len; i++){
4553                     var o = ss[i];
4554                     if(value.test(o[property])){
4555                         data.push(o);
4556                     }
4557                 }
4558             } else{
4559                 return;
4560             }
4561             this.jsonData = data;
4562             this.refresh();
4563         }
4564     },
4565
4566 /**
4567  * Filter by a function. The passed function will be called with each
4568  * object in the current dataset. If the function returns true the value is kept,
4569  * otherwise it is filtered.
4570  * @param {Function} fn
4571  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4572  */
4573     filterBy : function(fn, scope){
4574         if(this.jsonData){
4575             var data = [];
4576             var ss = this.snapshot;
4577             for(var i = 0, len = ss.length; i < len; i++){
4578                 var o = ss[i];
4579                 if(fn.call(scope || this, o)){
4580                     data.push(o);
4581                 }
4582             }
4583             this.jsonData = data;
4584             this.refresh();
4585         }
4586     },
4587
4588 /**
4589  * Clears the current filter.
4590  */
4591     clearFilter : function(){
4592         if(this.snapshot && this.jsonData != this.snapshot){
4593             this.jsonData = this.snapshot;
4594             this.refresh();
4595         }
4596     },
4597
4598
4599 /**
4600  * Sorts the data for this view and refreshes it.
4601  * @param {String} property A property on your JSON objects to sort on
4602  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4603  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4604  */
4605     sort : function(property, dir, sortType){
4606         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4607         if(this.jsonData){
4608             var p = property;
4609             var dsc = dir && dir.toLowerCase() == "desc";
4610             var f = function(o1, o2){
4611                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4612                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4613                 ;
4614                 if(v1 < v2){
4615                     return dsc ? +1 : -1;
4616                 } else if(v1 > v2){
4617                     return dsc ? -1 : +1;
4618                 } else{
4619                     return 0;
4620                 }
4621             };
4622             this.jsonData.sort(f);
4623             this.refresh();
4624             if(this.jsonData != this.snapshot){
4625                 this.snapshot.sort(f);
4626             }
4627         }
4628     }
4629 });/*
4630  * Based on:
4631  * Ext JS Library 1.1.1
4632  * Copyright(c) 2006-2007, Ext JS, LLC.
4633  *
4634  * Originally Released Under LGPL - original licence link has changed is not relivant.
4635  *
4636  * Fork - LGPL
4637  * <script type="text/javascript">
4638  */
4639  
4640
4641 /**
4642  * @class Roo.ColorPalette
4643  * @extends Roo.Component
4644  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4645  * Here's an example of typical usage:
4646  * <pre><code>
4647 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4648 cp.render('my-div');
4649
4650 cp.on('select', function(palette, selColor){
4651     // do something with selColor
4652 });
4653 </code></pre>
4654  * @constructor
4655  * Create a new ColorPalette
4656  * @param {Object} config The config object
4657  */
4658 Roo.ColorPalette = function(config){
4659     Roo.ColorPalette.superclass.constructor.call(this, config);
4660     this.addEvents({
4661         /**
4662              * @event select
4663              * Fires when a color is selected
4664              * @param {ColorPalette} this
4665              * @param {String} color The 6-digit color hex code (without the # symbol)
4666              */
4667         select: true
4668     });
4669
4670     if(this.handler){
4671         this.on("select", this.handler, this.scope, true);
4672     }
4673 };
4674 Roo.extend(Roo.ColorPalette, Roo.Component, {
4675     /**
4676      * @cfg {String} itemCls
4677      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4678      */
4679     itemCls : "x-color-palette",
4680     /**
4681      * @cfg {String} value
4682      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4683      * the hex codes are case-sensitive.
4684      */
4685     value : null,
4686     clickEvent:'click',
4687     // private
4688     ctype: "Roo.ColorPalette",
4689
4690     /**
4691      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4692      */
4693     allowReselect : false,
4694
4695     /**
4696      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4697      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4698      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4699      * of colors with the width setting until the box is symmetrical.</p>
4700      * <p>You can override individual colors if needed:</p>
4701      * <pre><code>
4702 var cp = new Roo.ColorPalette();
4703 cp.colors[0] = "FF0000";  // change the first box to red
4704 </code></pre>
4705
4706 Or you can provide a custom array of your own for complete control:
4707 <pre><code>
4708 var cp = new Roo.ColorPalette();
4709 cp.colors = ["000000", "993300", "333300"];
4710 </code></pre>
4711      * @type Array
4712      */
4713     colors : [
4714         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4715         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4716         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4717         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4718         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4719     ],
4720
4721     // private
4722     onRender : function(container, position){
4723         var t = new Roo.MasterTemplate(
4724             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4725         );
4726         var c = this.colors;
4727         for(var i = 0, len = c.length; i < len; i++){
4728             t.add([c[i]]);
4729         }
4730         var el = document.createElement("div");
4731         el.className = this.itemCls;
4732         t.overwrite(el);
4733         container.dom.insertBefore(el, position);
4734         this.el = Roo.get(el);
4735         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4736         if(this.clickEvent != 'click'){
4737             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4738         }
4739     },
4740
4741     // private
4742     afterRender : function(){
4743         Roo.ColorPalette.superclass.afterRender.call(this);
4744         if(this.value){
4745             var s = this.value;
4746             this.value = null;
4747             this.select(s);
4748         }
4749     },
4750
4751     // private
4752     handleClick : function(e, t){
4753         e.preventDefault();
4754         if(!this.disabled){
4755             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4756             this.select(c.toUpperCase());
4757         }
4758     },
4759
4760     /**
4761      * Selects the specified color in the palette (fires the select event)
4762      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4763      */
4764     select : function(color){
4765         color = color.replace("#", "");
4766         if(color != this.value || this.allowReselect){
4767             var el = this.el;
4768             if(this.value){
4769                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4770             }
4771             el.child("a.color-"+color).addClass("x-color-palette-sel");
4772             this.value = color;
4773             this.fireEvent("select", this, color);
4774         }
4775     }
4776 });/*
4777  * Based on:
4778  * Ext JS Library 1.1.1
4779  * Copyright(c) 2006-2007, Ext JS, LLC.
4780  *
4781  * Originally Released Under LGPL - original licence link has changed is not relivant.
4782  *
4783  * Fork - LGPL
4784  * <script type="text/javascript">
4785  */
4786  
4787 /**
4788  * @class Roo.DatePicker
4789  * @extends Roo.Component
4790  * Simple date picker class.
4791  * @constructor
4792  * Create a new DatePicker
4793  * @param {Object} config The config object
4794  */
4795 Roo.DatePicker = function(config){
4796     Roo.DatePicker.superclass.constructor.call(this, config);
4797
4798     this.value = config && config.value ?
4799                  config.value.clearTime() : new Date().clearTime();
4800
4801     this.addEvents({
4802         /**
4803              * @event select
4804              * Fires when a date is selected
4805              * @param {DatePicker} this
4806              * @param {Date} date The selected date
4807              */
4808         'select': true,
4809         /**
4810              * @event monthchange
4811              * Fires when the displayed month changes 
4812              * @param {DatePicker} this
4813              * @param {Date} date The selected month
4814              */
4815         'monthchange': true
4816     });
4817
4818     if(this.handler){
4819         this.on("select", this.handler,  this.scope || this);
4820     }
4821     // build the disabledDatesRE
4822     if(!this.disabledDatesRE && this.disabledDates){
4823         var dd = this.disabledDates;
4824         var re = "(?:";
4825         for(var i = 0; i < dd.length; i++){
4826             re += dd[i];
4827             if(i != dd.length-1) {
4828                 re += "|";
4829             }
4830         }
4831         this.disabledDatesRE = new RegExp(re + ")");
4832     }
4833 };
4834
4835 Roo.extend(Roo.DatePicker, Roo.Component, {
4836     /**
4837      * @cfg {String} todayText
4838      * The text to display on the button that selects the current date (defaults to "Today")
4839      */
4840     todayText : "Today",
4841     /**
4842      * @cfg {String} okText
4843      * The text to display on the ok button
4844      */
4845     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4846     /**
4847      * @cfg {String} cancelText
4848      * The text to display on the cancel button
4849      */
4850     cancelText : "Cancel",
4851     /**
4852      * @cfg {String} todayTip
4853      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4854      */
4855     todayTip : "{0} (Spacebar)",
4856     /**
4857      * @cfg {Date} minDate
4858      * Minimum allowable date (JavaScript date object, defaults to null)
4859      */
4860     minDate : null,
4861     /**
4862      * @cfg {Date} maxDate
4863      * Maximum allowable date (JavaScript date object, defaults to null)
4864      */
4865     maxDate : null,
4866     /**
4867      * @cfg {String} minText
4868      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4869      */
4870     minText : "This date is before the minimum date",
4871     /**
4872      * @cfg {String} maxText
4873      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4874      */
4875     maxText : "This date is after the maximum date",
4876     /**
4877      * @cfg {String} format
4878      * The default date format string which can be overriden for localization support.  The format must be
4879      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4880      */
4881     format : "m/d/y",
4882     /**
4883      * @cfg {Array} disabledDays
4884      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4885      */
4886     disabledDays : null,
4887     /**
4888      * @cfg {String} disabledDaysText
4889      * The tooltip to display when the date falls on a disabled day (defaults to "")
4890      */
4891     disabledDaysText : "",
4892     /**
4893      * @cfg {RegExp} disabledDatesRE
4894      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4895      */
4896     disabledDatesRE : null,
4897     /**
4898      * @cfg {String} disabledDatesText
4899      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4900      */
4901     disabledDatesText : "",
4902     /**
4903      * @cfg {Boolean} constrainToViewport
4904      * True to constrain the date picker to the viewport (defaults to true)
4905      */
4906     constrainToViewport : true,
4907     /**
4908      * @cfg {Array} monthNames
4909      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4910      */
4911     monthNames : Date.monthNames,
4912     /**
4913      * @cfg {Array} dayNames
4914      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4915      */
4916     dayNames : Date.dayNames,
4917     /**
4918      * @cfg {String} nextText
4919      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4920      */
4921     nextText: 'Next Month (Control+Right)',
4922     /**
4923      * @cfg {String} prevText
4924      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4925      */
4926     prevText: 'Previous Month (Control+Left)',
4927     /**
4928      * @cfg {String} monthYearText
4929      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4930      */
4931     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4932     /**
4933      * @cfg {Number} startDay
4934      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4935      */
4936     startDay : 0,
4937     /**
4938      * @cfg {Bool} showClear
4939      * Show a clear button (usefull for date form elements that can be blank.)
4940      */
4941     
4942     showClear: false,
4943     
4944     /**
4945      * Sets the value of the date field
4946      * @param {Date} value The date to set
4947      */
4948     setValue : function(value){
4949         var old = this.value;
4950         
4951         if (typeof(value) == 'string') {
4952          
4953             value = Date.parseDate(value, this.format);
4954         }
4955         if (!value) {
4956             value = new Date();
4957         }
4958         
4959         this.value = value.clearTime(true);
4960         if(this.el){
4961             this.update(this.value);
4962         }
4963     },
4964
4965     /**
4966      * Gets the current selected value of the date field
4967      * @return {Date} The selected date
4968      */
4969     getValue : function(){
4970         return this.value;
4971     },
4972
4973     // private
4974     focus : function(){
4975         if(this.el){
4976             this.update(this.activeDate);
4977         }
4978     },
4979
4980     // privateval
4981     onRender : function(container, position){
4982         
4983         var m = [
4984              '<table cellspacing="0">',
4985                 '<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>',
4986                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4987         var dn = this.dayNames;
4988         for(var i = 0; i < 7; i++){
4989             var d = this.startDay+i;
4990             if(d > 6){
4991                 d = d-7;
4992             }
4993             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4994         }
4995         m[m.length] = "</tr></thead><tbody><tr>";
4996         for(var i = 0; i < 42; i++) {
4997             if(i % 7 == 0 && i != 0){
4998                 m[m.length] = "</tr><tr>";
4999             }
5000             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5001         }
5002         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5003             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5004
5005         var el = document.createElement("div");
5006         el.className = "x-date-picker";
5007         el.innerHTML = m.join("");
5008
5009         container.dom.insertBefore(el, position);
5010
5011         this.el = Roo.get(el);
5012         this.eventEl = Roo.get(el.firstChild);
5013
5014         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5015             handler: this.showPrevMonth,
5016             scope: this,
5017             preventDefault:true,
5018             stopDefault:true
5019         });
5020
5021         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5022             handler: this.showNextMonth,
5023             scope: this,
5024             preventDefault:true,
5025             stopDefault:true
5026         });
5027
5028         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5029
5030         this.monthPicker = this.el.down('div.x-date-mp');
5031         this.monthPicker.enableDisplayMode('block');
5032         
5033         var kn = new Roo.KeyNav(this.eventEl, {
5034             "left" : function(e){
5035                 e.ctrlKey ?
5036                     this.showPrevMonth() :
5037                     this.update(this.activeDate.add("d", -1));
5038             },
5039
5040             "right" : function(e){
5041                 e.ctrlKey ?
5042                     this.showNextMonth() :
5043                     this.update(this.activeDate.add("d", 1));
5044             },
5045
5046             "up" : function(e){
5047                 e.ctrlKey ?
5048                     this.showNextYear() :
5049                     this.update(this.activeDate.add("d", -7));
5050             },
5051
5052             "down" : function(e){
5053                 e.ctrlKey ?
5054                     this.showPrevYear() :
5055                     this.update(this.activeDate.add("d", 7));
5056             },
5057
5058             "pageUp" : function(e){
5059                 this.showNextMonth();
5060             },
5061
5062             "pageDown" : function(e){
5063                 this.showPrevMonth();
5064             },
5065
5066             "enter" : function(e){
5067                 e.stopPropagation();
5068                 return true;
5069             },
5070
5071             scope : this
5072         });
5073
5074         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5075
5076         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5077
5078         this.el.unselectable();
5079         
5080         this.cells = this.el.select("table.x-date-inner tbody td");
5081         this.textNodes = this.el.query("table.x-date-inner tbody span");
5082
5083         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5084             text: "&#160;",
5085             tooltip: this.monthYearText
5086         });
5087
5088         this.mbtn.on('click', this.showMonthPicker, this);
5089         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5090
5091
5092         var today = (new Date()).dateFormat(this.format);
5093         
5094         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5095         if (this.showClear) {
5096             baseTb.add( new Roo.Toolbar.Fill());
5097         }
5098         baseTb.add({
5099             text: String.format(this.todayText, today),
5100             tooltip: String.format(this.todayTip, today),
5101             handler: this.selectToday,
5102             scope: this
5103         });
5104         
5105         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5106             
5107         //});
5108         if (this.showClear) {
5109             
5110             baseTb.add( new Roo.Toolbar.Fill());
5111             baseTb.add({
5112                 text: '&#160;',
5113                 cls: 'x-btn-icon x-btn-clear',
5114                 handler: function() {
5115                     //this.value = '';
5116                     this.fireEvent("select", this, '');
5117                 },
5118                 scope: this
5119             });
5120         }
5121         
5122         
5123         if(Roo.isIE){
5124             this.el.repaint();
5125         }
5126         this.update(this.value);
5127     },
5128
5129     createMonthPicker : function(){
5130         if(!this.monthPicker.dom.firstChild){
5131             var buf = ['<table border="0" cellspacing="0">'];
5132             for(var i = 0; i < 6; i++){
5133                 buf.push(
5134                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5135                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5136                     i == 0 ?
5137                     '<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>' :
5138                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5139                 );
5140             }
5141             buf.push(
5142                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5143                     this.okText,
5144                     '</button><button type="button" class="x-date-mp-cancel">',
5145                     this.cancelText,
5146                     '</button></td></tr>',
5147                 '</table>'
5148             );
5149             this.monthPicker.update(buf.join(''));
5150             this.monthPicker.on('click', this.onMonthClick, this);
5151             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5152
5153             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5154             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5155
5156             this.mpMonths.each(function(m, a, i){
5157                 i += 1;
5158                 if((i%2) == 0){
5159                     m.dom.xmonth = 5 + Math.round(i * .5);
5160                 }else{
5161                     m.dom.xmonth = Math.round((i-1) * .5);
5162                 }
5163             });
5164         }
5165     },
5166
5167     showMonthPicker : function(){
5168         this.createMonthPicker();
5169         var size = this.el.getSize();
5170         this.monthPicker.setSize(size);
5171         this.monthPicker.child('table').setSize(size);
5172
5173         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5174         this.updateMPMonth(this.mpSelMonth);
5175         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5176         this.updateMPYear(this.mpSelYear);
5177
5178         this.monthPicker.slideIn('t', {duration:.2});
5179     },
5180
5181     updateMPYear : function(y){
5182         this.mpyear = y;
5183         var ys = this.mpYears.elements;
5184         for(var i = 1; i <= 10; i++){
5185             var td = ys[i-1], y2;
5186             if((i%2) == 0){
5187                 y2 = y + Math.round(i * .5);
5188                 td.firstChild.innerHTML = y2;
5189                 td.xyear = y2;
5190             }else{
5191                 y2 = y - (5-Math.round(i * .5));
5192                 td.firstChild.innerHTML = y2;
5193                 td.xyear = y2;
5194             }
5195             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5196         }
5197     },
5198
5199     updateMPMonth : function(sm){
5200         this.mpMonths.each(function(m, a, i){
5201             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5202         });
5203     },
5204
5205     selectMPMonth: function(m){
5206         
5207     },
5208
5209     onMonthClick : function(e, t){
5210         e.stopEvent();
5211         var el = new Roo.Element(t), pn;
5212         if(el.is('button.x-date-mp-cancel')){
5213             this.hideMonthPicker();
5214         }
5215         else if(el.is('button.x-date-mp-ok')){
5216             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5217             this.hideMonthPicker();
5218         }
5219         else if(pn = el.up('td.x-date-mp-month', 2)){
5220             this.mpMonths.removeClass('x-date-mp-sel');
5221             pn.addClass('x-date-mp-sel');
5222             this.mpSelMonth = pn.dom.xmonth;
5223         }
5224         else if(pn = el.up('td.x-date-mp-year', 2)){
5225             this.mpYears.removeClass('x-date-mp-sel');
5226             pn.addClass('x-date-mp-sel');
5227             this.mpSelYear = pn.dom.xyear;
5228         }
5229         else if(el.is('a.x-date-mp-prev')){
5230             this.updateMPYear(this.mpyear-10);
5231         }
5232         else if(el.is('a.x-date-mp-next')){
5233             this.updateMPYear(this.mpyear+10);
5234         }
5235     },
5236
5237     onMonthDblClick : function(e, t){
5238         e.stopEvent();
5239         var el = new Roo.Element(t), pn;
5240         if(pn = el.up('td.x-date-mp-month', 2)){
5241             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5242             this.hideMonthPicker();
5243         }
5244         else if(pn = el.up('td.x-date-mp-year', 2)){
5245             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5246             this.hideMonthPicker();
5247         }
5248     },
5249
5250     hideMonthPicker : function(disableAnim){
5251         if(this.monthPicker){
5252             if(disableAnim === true){
5253                 this.monthPicker.hide();
5254             }else{
5255                 this.monthPicker.slideOut('t', {duration:.2});
5256             }
5257         }
5258     },
5259
5260     // private
5261     showPrevMonth : function(e){
5262         this.update(this.activeDate.add("mo", -1));
5263     },
5264
5265     // private
5266     showNextMonth : function(e){
5267         this.update(this.activeDate.add("mo", 1));
5268     },
5269
5270     // private
5271     showPrevYear : function(){
5272         this.update(this.activeDate.add("y", -1));
5273     },
5274
5275     // private
5276     showNextYear : function(){
5277         this.update(this.activeDate.add("y", 1));
5278     },
5279
5280     // private
5281     handleMouseWheel : function(e){
5282         var delta = e.getWheelDelta();
5283         if(delta > 0){
5284             this.showPrevMonth();
5285             e.stopEvent();
5286         } else if(delta < 0){
5287             this.showNextMonth();
5288             e.stopEvent();
5289         }
5290     },
5291
5292     // private
5293     handleDateClick : function(e, t){
5294         e.stopEvent();
5295         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5296             this.setValue(new Date(t.dateValue));
5297             this.fireEvent("select", this, this.value);
5298         }
5299     },
5300
5301     // private
5302     selectToday : function(){
5303         this.setValue(new Date().clearTime());
5304         this.fireEvent("select", this, this.value);
5305     },
5306
5307     // private
5308     update : function(date)
5309     {
5310         var vd = this.activeDate;
5311         this.activeDate = date;
5312         if(vd && this.el){
5313             var t = date.getTime();
5314             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5315                 this.cells.removeClass("x-date-selected");
5316                 this.cells.each(function(c){
5317                    if(c.dom.firstChild.dateValue == t){
5318                        c.addClass("x-date-selected");
5319                        setTimeout(function(){
5320                             try{c.dom.firstChild.focus();}catch(e){}
5321                        }, 50);
5322                        return false;
5323                    }
5324                 });
5325                 return;
5326             }
5327         }
5328         
5329         var days = date.getDaysInMonth();
5330         var firstOfMonth = date.getFirstDateOfMonth();
5331         var startingPos = firstOfMonth.getDay()-this.startDay;
5332
5333         if(startingPos <= this.startDay){
5334             startingPos += 7;
5335         }
5336
5337         var pm = date.add("mo", -1);
5338         var prevStart = pm.getDaysInMonth()-startingPos;
5339
5340         var cells = this.cells.elements;
5341         var textEls = this.textNodes;
5342         days += startingPos;
5343
5344         // convert everything to numbers so it's fast
5345         var day = 86400000;
5346         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5347         var today = new Date().clearTime().getTime();
5348         var sel = date.clearTime().getTime();
5349         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5350         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5351         var ddMatch = this.disabledDatesRE;
5352         var ddText = this.disabledDatesText;
5353         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5354         var ddaysText = this.disabledDaysText;
5355         var format = this.format;
5356
5357         var setCellClass = function(cal, cell){
5358             cell.title = "";
5359             var t = d.getTime();
5360             cell.firstChild.dateValue = t;
5361             if(t == today){
5362                 cell.className += " x-date-today";
5363                 cell.title = cal.todayText;
5364             }
5365             if(t == sel){
5366                 cell.className += " x-date-selected";
5367                 setTimeout(function(){
5368                     try{cell.firstChild.focus();}catch(e){}
5369                 }, 50);
5370             }
5371             // disabling
5372             if(t < min) {
5373                 cell.className = " x-date-disabled";
5374                 cell.title = cal.minText;
5375                 return;
5376             }
5377             if(t > max) {
5378                 cell.className = " x-date-disabled";
5379                 cell.title = cal.maxText;
5380                 return;
5381             }
5382             if(ddays){
5383                 if(ddays.indexOf(d.getDay()) != -1){
5384                     cell.title = ddaysText;
5385                     cell.className = " x-date-disabled";
5386                 }
5387             }
5388             if(ddMatch && format){
5389                 var fvalue = d.dateFormat(format);
5390                 if(ddMatch.test(fvalue)){
5391                     cell.title = ddText.replace("%0", fvalue);
5392                     cell.className = " x-date-disabled";
5393                 }
5394             }
5395         };
5396
5397         var i = 0;
5398         for(; i < startingPos; i++) {
5399             textEls[i].innerHTML = (++prevStart);
5400             d.setDate(d.getDate()+1);
5401             cells[i].className = "x-date-prevday";
5402             setCellClass(this, cells[i]);
5403         }
5404         for(; i < days; i++){
5405             intDay = i - startingPos + 1;
5406             textEls[i].innerHTML = (intDay);
5407             d.setDate(d.getDate()+1);
5408             cells[i].className = "x-date-active";
5409             setCellClass(this, cells[i]);
5410         }
5411         var extraDays = 0;
5412         for(; i < 42; i++) {
5413              textEls[i].innerHTML = (++extraDays);
5414              d.setDate(d.getDate()+1);
5415              cells[i].className = "x-date-nextday";
5416              setCellClass(this, cells[i]);
5417         }
5418
5419         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5420         this.fireEvent('monthchange', this, date);
5421         
5422         if(!this.internalRender){
5423             var main = this.el.dom.firstChild;
5424             var w = main.offsetWidth;
5425             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5426             Roo.fly(main).setWidth(w);
5427             this.internalRender = true;
5428             // opera does not respect the auto grow header center column
5429             // then, after it gets a width opera refuses to recalculate
5430             // without a second pass
5431             if(Roo.isOpera && !this.secondPass){
5432                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5433                 this.secondPass = true;
5434                 this.update.defer(10, this, [date]);
5435             }
5436         }
5437         
5438         
5439     }
5440 });        /*
5441  * Based on:
5442  * Ext JS Library 1.1.1
5443  * Copyright(c) 2006-2007, Ext JS, LLC.
5444  *
5445  * Originally Released Under LGPL - original licence link has changed is not relivant.
5446  *
5447  * Fork - LGPL
5448  * <script type="text/javascript">
5449  */
5450 /**
5451  * @class Roo.TabPanel
5452  * @extends Roo.util.Observable
5453  * A lightweight tab container.
5454  * <br><br>
5455  * Usage:
5456  * <pre><code>
5457 // basic tabs 1, built from existing content
5458 var tabs = new Roo.TabPanel("tabs1");
5459 tabs.addTab("script", "View Script");
5460 tabs.addTab("markup", "View Markup");
5461 tabs.activate("script");
5462
5463 // more advanced tabs, built from javascript
5464 var jtabs = new Roo.TabPanel("jtabs");
5465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5466
5467 // set up the UpdateManager
5468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5469 var updater = tab2.getUpdateManager();
5470 updater.setDefaultUrl("ajax1.htm");
5471 tab2.on('activate', updater.refresh, updater, true);
5472
5473 // Use setUrl for Ajax loading
5474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5475 tab3.setUrl("ajax2.htm", null, true);
5476
5477 // Disabled tab
5478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5479 tab4.disable();
5480
5481 jtabs.activate("jtabs-1");
5482  * </code></pre>
5483  * @constructor
5484  * Create a new TabPanel.
5485  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5486  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5487  */
5488 Roo.TabPanel = function(container, config){
5489     /**
5490     * The container element for this TabPanel.
5491     * @type Roo.Element
5492     */
5493     this.el = Roo.get(container, true);
5494     if(config){
5495         if(typeof config == "boolean"){
5496             this.tabPosition = config ? "bottom" : "top";
5497         }else{
5498             Roo.apply(this, config);
5499         }
5500     }
5501     if(this.tabPosition == "bottom"){
5502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5503         this.el.addClass("x-tabs-bottom");
5504     }
5505     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5506     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5507     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5508     if(Roo.isIE){
5509         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5510     }
5511     if(this.tabPosition != "bottom"){
5512         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5513          * @type Roo.Element
5514          */
5515         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5516         this.el.addClass("x-tabs-top");
5517     }
5518     this.items = [];
5519
5520     this.bodyEl.setStyle("position", "relative");
5521
5522     this.active = null;
5523     this.activateDelegate = this.activate.createDelegate(this);
5524
5525     this.addEvents({
5526         /**
5527          * @event tabchange
5528          * Fires when the active tab changes
5529          * @param {Roo.TabPanel} this
5530          * @param {Roo.TabPanelItem} activePanel The new active tab
5531          */
5532         "tabchange": true,
5533         /**
5534          * @event beforetabchange
5535          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5536          * @param {Roo.TabPanel} this
5537          * @param {Object} e Set cancel to true on this object to cancel the tab change
5538          * @param {Roo.TabPanelItem} tab The tab being changed to
5539          */
5540         "beforetabchange" : true
5541     });
5542
5543     Roo.EventManager.onWindowResize(this.onResize, this);
5544     this.cpad = this.el.getPadding("lr");
5545     this.hiddenCount = 0;
5546
5547
5548     // toolbar on the tabbar support...
5549     if (this.toolbar) {
5550         var tcfg = this.toolbar;
5551         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5552         this.toolbar = new Roo.Toolbar(tcfg);
5553         if (Roo.isSafari) {
5554             var tbl = tcfg.container.child('table', true);
5555             tbl.setAttribute('width', '100%');
5556         }
5557         
5558     }
5559    
5560
5561
5562     Roo.TabPanel.superclass.constructor.call(this);
5563 };
5564
5565 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5566     /*
5567      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5568      */
5569     tabPosition : "top",
5570     /*
5571      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5572      */
5573     currentTabWidth : 0,
5574     /*
5575      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5576      */
5577     minTabWidth : 40,
5578     /*
5579      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5580      */
5581     maxTabWidth : 250,
5582     /*
5583      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5584      */
5585     preferredTabWidth : 175,
5586     /*
5587      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5588      */
5589     resizeTabs : false,
5590     /*
5591      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5592      */
5593     monitorResize : true,
5594     /*
5595      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5596      */
5597     toolbar : false,
5598
5599     /**
5600      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5601      * @param {String} id The id of the div to use <b>or create</b>
5602      * @param {String} text The text for the tab
5603      * @param {String} content (optional) Content to put in the TabPanelItem body
5604      * @param {Boolean} closable (optional) True to create a close icon on the tab
5605      * @return {Roo.TabPanelItem} The created TabPanelItem
5606      */
5607     addTab : function(id, text, content, closable){
5608         var item = new Roo.TabPanelItem(this, id, text, closable);
5609         this.addTabItem(item);
5610         if(content){
5611             item.setContent(content);
5612         }
5613         return item;
5614     },
5615
5616     /**
5617      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5618      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5619      * @return {Roo.TabPanelItem}
5620      */
5621     getTab : function(id){
5622         return this.items[id];
5623     },
5624
5625     /**
5626      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5627      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5628      */
5629     hideTab : function(id){
5630         var t = this.items[id];
5631         if(!t.isHidden()){
5632            t.setHidden(true);
5633            this.hiddenCount++;
5634            this.autoSizeTabs();
5635         }
5636     },
5637
5638     /**
5639      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5640      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5641      */
5642     unhideTab : function(id){
5643         var t = this.items[id];
5644         if(t.isHidden()){
5645            t.setHidden(false);
5646            this.hiddenCount--;
5647            this.autoSizeTabs();
5648         }
5649     },
5650
5651     /**
5652      * Adds an existing {@link Roo.TabPanelItem}.
5653      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5654      */
5655     addTabItem : function(item){
5656         this.items[item.id] = item;
5657         this.items.push(item);
5658         if(this.resizeTabs){
5659            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5660            this.autoSizeTabs();
5661         }else{
5662             item.autoSize();
5663         }
5664     },
5665
5666     /**
5667      * Removes a {@link Roo.TabPanelItem}.
5668      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5669      */
5670     removeTab : function(id){
5671         var items = this.items;
5672         var tab = items[id];
5673         if(!tab) { return; }
5674         var index = items.indexOf(tab);
5675         if(this.active == tab && items.length > 1){
5676             var newTab = this.getNextAvailable(index);
5677             if(newTab) {
5678                 newTab.activate();
5679             }
5680         }
5681         this.stripEl.dom.removeChild(tab.pnode.dom);
5682         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5683             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5684         }
5685         items.splice(index, 1);
5686         delete this.items[tab.id];
5687         tab.fireEvent("close", tab);
5688         tab.purgeListeners();
5689         this.autoSizeTabs();
5690     },
5691
5692     getNextAvailable : function(start){
5693         var items = this.items;
5694         var index = start;
5695         // look for a next tab that will slide over to
5696         // replace the one being removed
5697         while(index < items.length){
5698             var item = items[++index];
5699             if(item && !item.isHidden()){
5700                 return item;
5701             }
5702         }
5703         // if one isn't found select the previous tab (on the left)
5704         index = start;
5705         while(index >= 0){
5706             var item = items[--index];
5707             if(item && !item.isHidden()){
5708                 return item;
5709             }
5710         }
5711         return null;
5712     },
5713
5714     /**
5715      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5716      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5717      */
5718     disableTab : function(id){
5719         var tab = this.items[id];
5720         if(tab && this.active != tab){
5721             tab.disable();
5722         }
5723     },
5724
5725     /**
5726      * Enables a {@link Roo.TabPanelItem} that is disabled.
5727      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5728      */
5729     enableTab : function(id){
5730         var tab = this.items[id];
5731         tab.enable();
5732     },
5733
5734     /**
5735      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5736      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5737      * @return {Roo.TabPanelItem} The TabPanelItem.
5738      */
5739     activate : function(id){
5740         var tab = this.items[id];
5741         if(!tab){
5742             return null;
5743         }
5744         if(tab == this.active || tab.disabled){
5745             return tab;
5746         }
5747         var e = {};
5748         this.fireEvent("beforetabchange", this, e, tab);
5749         if(e.cancel !== true && !tab.disabled){
5750             if(this.active){
5751                 this.active.hide();
5752             }
5753             this.active = this.items[id];
5754             this.active.show();
5755             this.fireEvent("tabchange", this, this.active);
5756         }
5757         return tab;
5758     },
5759
5760     /**
5761      * Gets the active {@link Roo.TabPanelItem}.
5762      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5763      */
5764     getActiveTab : function(){
5765         return this.active;
5766     },
5767
5768     /**
5769      * Updates the tab body element to fit the height of the container element
5770      * for overflow scrolling
5771      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5772      */
5773     syncHeight : function(targetHeight){
5774         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5775         var bm = this.bodyEl.getMargins();
5776         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5777         this.bodyEl.setHeight(newHeight);
5778         return newHeight;
5779     },
5780
5781     onResize : function(){
5782         if(this.monitorResize){
5783             this.autoSizeTabs();
5784         }
5785     },
5786
5787     /**
5788      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5789      */
5790     beginUpdate : function(){
5791         this.updating = true;
5792     },
5793
5794     /**
5795      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5796      */
5797     endUpdate : function(){
5798         this.updating = false;
5799         this.autoSizeTabs();
5800     },
5801
5802     /**
5803      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     autoSizeTabs : function(){
5806         var count = this.items.length;
5807         var vcount = count - this.hiddenCount;
5808         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5809             return;
5810         }
5811         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5812         var availWidth = Math.floor(w / vcount);
5813         var b = this.stripBody;
5814         if(b.getWidth() > w){
5815             var tabs = this.items;
5816             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5817             if(availWidth < this.minTabWidth){
5818                 /*if(!this.sleft){    // incomplete scrolling code
5819                     this.createScrollButtons();
5820                 }
5821                 this.showScroll();
5822                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5823             }
5824         }else{
5825             if(this.currentTabWidth < this.preferredTabWidth){
5826                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5827             }
5828         }
5829     },
5830
5831     /**
5832      * Returns the number of tabs in this TabPanel.
5833      * @return {Number}
5834      */
5835      getCount : function(){
5836          return this.items.length;
5837      },
5838
5839     /**
5840      * Resizes all the tabs to the passed width
5841      * @param {Number} The new width
5842      */
5843     setTabWidth : function(width){
5844         this.currentTabWidth = width;
5845         for(var i = 0, len = this.items.length; i < len; i++) {
5846                 if(!this.items[i].isHidden()) {
5847                 this.items[i].setWidth(width);
5848             }
5849         }
5850     },
5851
5852     /**
5853      * Destroys this TabPanel
5854      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5855      */
5856     destroy : function(removeEl){
5857         Roo.EventManager.removeResizeListener(this.onResize, this);
5858         for(var i = 0, len = this.items.length; i < len; i++){
5859             this.items[i].purgeListeners();
5860         }
5861         if(removeEl === true){
5862             this.el.update("");
5863             this.el.remove();
5864         }
5865     }
5866 });
5867
5868 /**
5869  * @class Roo.TabPanelItem
5870  * @extends Roo.util.Observable
5871  * Represents an individual item (tab plus body) in a TabPanel.
5872  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5873  * @param {String} id The id of this TabPanelItem
5874  * @param {String} text The text for the tab of this TabPanelItem
5875  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5876  */
5877 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5878     /**
5879      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5880      * @type Roo.TabPanel
5881      */
5882     this.tabPanel = tabPanel;
5883     /**
5884      * The id for this TabPanelItem
5885      * @type String
5886      */
5887     this.id = id;
5888     /** @private */
5889     this.disabled = false;
5890     /** @private */
5891     this.text = text;
5892     /** @private */
5893     this.loaded = false;
5894     this.closable = closable;
5895
5896     /**
5897      * The body element for this TabPanelItem.
5898      * @type Roo.Element
5899      */
5900     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5901     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5902     this.bodyEl.setStyle("display", "block");
5903     this.bodyEl.setStyle("zoom", "1");
5904     this.hideAction();
5905
5906     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5907     /** @private */
5908     this.el = Roo.get(els.el, true);
5909     this.inner = Roo.get(els.inner, true);
5910     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5911     this.pnode = Roo.get(els.el.parentNode, true);
5912     this.el.on("mousedown", this.onTabMouseDown, this);
5913     this.el.on("click", this.onTabClick, this);
5914     /** @private */
5915     if(closable){
5916         var c = Roo.get(els.close, true);
5917         c.dom.title = this.closeText;
5918         c.addClassOnOver("close-over");
5919         c.on("click", this.closeClick, this);
5920      }
5921
5922     this.addEvents({
5923          /**
5924          * @event activate
5925          * Fires when this tab becomes the active tab.
5926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5927          * @param {Roo.TabPanelItem} this
5928          */
5929         "activate": true,
5930         /**
5931          * @event beforeclose
5932          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5933          * @param {Roo.TabPanelItem} this
5934          * @param {Object} e Set cancel to true on this object to cancel the close.
5935          */
5936         "beforeclose": true,
5937         /**
5938          * @event close
5939          * Fires when this tab is closed.
5940          * @param {Roo.TabPanelItem} this
5941          */
5942          "close": true,
5943         /**
5944          * @event deactivate
5945          * Fires when this tab is no longer the active tab.
5946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5947          * @param {Roo.TabPanelItem} this
5948          */
5949          "deactivate" : true
5950     });
5951     this.hidden = false;
5952
5953     Roo.TabPanelItem.superclass.constructor.call(this);
5954 };
5955
5956 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5957     purgeListeners : function(){
5958        Roo.util.Observable.prototype.purgeListeners.call(this);
5959        this.el.removeAllListeners();
5960     },
5961     /**
5962      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5963      */
5964     show : function(){
5965         this.pnode.addClass("on");
5966         this.showAction();
5967         if(Roo.isOpera){
5968             this.tabPanel.stripWrap.repaint();
5969         }
5970         this.fireEvent("activate", this.tabPanel, this);
5971     },
5972
5973     /**
5974      * Returns true if this tab is the active tab.
5975      * @return {Boolean}
5976      */
5977     isActive : function(){
5978         return this.tabPanel.getActiveTab() == this;
5979     },
5980
5981     /**
5982      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5983      */
5984     hide : function(){
5985         this.pnode.removeClass("on");
5986         this.hideAction();
5987         this.fireEvent("deactivate", this.tabPanel, this);
5988     },
5989
5990     hideAction : function(){
5991         this.bodyEl.hide();
5992         this.bodyEl.setStyle("position", "absolute");
5993         this.bodyEl.setLeft("-20000px");
5994         this.bodyEl.setTop("-20000px");
5995     },
5996
5997     showAction : function(){
5998         this.bodyEl.setStyle("position", "relative");
5999         this.bodyEl.setTop("");
6000         this.bodyEl.setLeft("");
6001         this.bodyEl.show();
6002     },
6003
6004     /**
6005      * Set the tooltip for the tab.
6006      * @param {String} tooltip The tab's tooltip
6007      */
6008     setTooltip : function(text){
6009         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6010             this.textEl.dom.qtip = text;
6011             this.textEl.dom.removeAttribute('title');
6012         }else{
6013             this.textEl.dom.title = text;
6014         }
6015     },
6016
6017     onTabClick : function(e){
6018         e.preventDefault();
6019         this.tabPanel.activate(this.id);
6020     },
6021
6022     onTabMouseDown : function(e){
6023         e.preventDefault();
6024         this.tabPanel.activate(this.id);
6025     },
6026
6027     getWidth : function(){
6028         return this.inner.getWidth();
6029     },
6030
6031     setWidth : function(width){
6032         var iwidth = width - this.pnode.getPadding("lr");
6033         this.inner.setWidth(iwidth);
6034         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6035         this.pnode.setWidth(width);
6036     },
6037
6038     /**
6039      * Show or hide the tab
6040      * @param {Boolean} hidden True to hide or false to show.
6041      */
6042     setHidden : function(hidden){
6043         this.hidden = hidden;
6044         this.pnode.setStyle("display", hidden ? "none" : "");
6045     },
6046
6047     /**
6048      * Returns true if this tab is "hidden"
6049      * @return {Boolean}
6050      */
6051     isHidden : function(){
6052         return this.hidden;
6053     },
6054
6055     /**
6056      * Returns the text for this tab
6057      * @return {String}
6058      */
6059     getText : function(){
6060         return this.text;
6061     },
6062
6063     autoSize : function(){
6064         //this.el.beginMeasure();
6065         this.textEl.setWidth(1);
6066         /*
6067          *  #2804 [new] Tabs in Roojs
6068          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6069          */
6070         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6071         //this.el.endMeasure();
6072     },
6073
6074     /**
6075      * Sets the text for the tab (Note: this also sets the tooltip text)
6076      * @param {String} text The tab's text and tooltip
6077      */
6078     setText : function(text){
6079         this.text = text;
6080         this.textEl.update(text);
6081         this.setTooltip(text);
6082         if(!this.tabPanel.resizeTabs){
6083             this.autoSize();
6084         }
6085     },
6086     /**
6087      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6088      */
6089     activate : function(){
6090         this.tabPanel.activate(this.id);
6091     },
6092
6093     /**
6094      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6095      */
6096     disable : function(){
6097         if(this.tabPanel.active != this){
6098             this.disabled = true;
6099             this.pnode.addClass("disabled");
6100         }
6101     },
6102
6103     /**
6104      * Enables this TabPanelItem if it was previously disabled.
6105      */
6106     enable : function(){
6107         this.disabled = false;
6108         this.pnode.removeClass("disabled");
6109     },
6110
6111     /**
6112      * Sets the content for this TabPanelItem.
6113      * @param {String} content The content
6114      * @param {Boolean} loadScripts true to look for and load scripts
6115      */
6116     setContent : function(content, loadScripts){
6117         this.bodyEl.update(content, loadScripts);
6118     },
6119
6120     /**
6121      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6122      * @return {Roo.UpdateManager} The UpdateManager
6123      */
6124     getUpdateManager : function(){
6125         return this.bodyEl.getUpdateManager();
6126     },
6127
6128     /**
6129      * Set a URL to be used to load the content for this TabPanelItem.
6130      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6131      * @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)
6132      * @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)
6133      * @return {Roo.UpdateManager} The UpdateManager
6134      */
6135     setUrl : function(url, params, loadOnce){
6136         if(this.refreshDelegate){
6137             this.un('activate', this.refreshDelegate);
6138         }
6139         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6140         this.on("activate", this.refreshDelegate);
6141         return this.bodyEl.getUpdateManager();
6142     },
6143
6144     /** @private */
6145     _handleRefresh : function(url, params, loadOnce){
6146         if(!loadOnce || !this.loaded){
6147             var updater = this.bodyEl.getUpdateManager();
6148             updater.update(url, params, this._setLoaded.createDelegate(this));
6149         }
6150     },
6151
6152     /**
6153      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6154      *   Will fail silently if the setUrl method has not been called.
6155      *   This does not activate the panel, just updates its content.
6156      */
6157     refresh : function(){
6158         if(this.refreshDelegate){
6159            this.loaded = false;
6160            this.refreshDelegate();
6161         }
6162     },
6163
6164     /** @private */
6165     _setLoaded : function(){
6166         this.loaded = true;
6167     },
6168
6169     /** @private */
6170     closeClick : function(e){
6171         var o = {};
6172         e.stopEvent();
6173         this.fireEvent("beforeclose", this, o);
6174         if(o.cancel !== true){
6175             this.tabPanel.removeTab(this.id);
6176         }
6177     },
6178     /**
6179      * The text displayed in the tooltip for the close icon.
6180      * @type String
6181      */
6182     closeText : "Close this tab"
6183 });
6184
6185 /** @private */
6186 Roo.TabPanel.prototype.createStrip = function(container){
6187     var strip = document.createElement("div");
6188     strip.className = "x-tabs-wrap";
6189     container.appendChild(strip);
6190     return strip;
6191 };
6192 /** @private */
6193 Roo.TabPanel.prototype.createStripList = function(strip){
6194     // div wrapper for retard IE
6195     // returns the "tr" element.
6196     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6197         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6198         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6199     return strip.firstChild.firstChild.firstChild.firstChild;
6200 };
6201 /** @private */
6202 Roo.TabPanel.prototype.createBody = function(container){
6203     var body = document.createElement("div");
6204     Roo.id(body, "tab-body");
6205     Roo.fly(body).addClass("x-tabs-body");
6206     container.appendChild(body);
6207     return body;
6208 };
6209 /** @private */
6210 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6211     var body = Roo.getDom(id);
6212     if(!body){
6213         body = document.createElement("div");
6214         body.id = id;
6215     }
6216     Roo.fly(body).addClass("x-tabs-item-body");
6217     bodyEl.insertBefore(body, bodyEl.firstChild);
6218     return body;
6219 };
6220 /** @private */
6221 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6222     var td = document.createElement("td");
6223     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6224     //stripEl.appendChild(td);
6225     if(closable){
6226         td.className = "x-tabs-closable";
6227         if(!this.closeTpl){
6228             this.closeTpl = new Roo.Template(
6229                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6230                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6231                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6232             );
6233         }
6234         var el = this.closeTpl.overwrite(td, {"text": text});
6235         var close = el.getElementsByTagName("div")[0];
6236         var inner = el.getElementsByTagName("em")[0];
6237         return {"el": el, "close": close, "inner": inner};
6238     } else {
6239         if(!this.tabTpl){
6240             this.tabTpl = new Roo.Template(
6241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6243             );
6244         }
6245         var el = this.tabTpl.overwrite(td, {"text": text});
6246         var inner = el.getElementsByTagName("em")[0];
6247         return {"el": el, "inner": inner};
6248     }
6249 };/*
6250  * Based on:
6251  * Ext JS Library 1.1.1
6252  * Copyright(c) 2006-2007, Ext JS, LLC.
6253  *
6254  * Originally Released Under LGPL - original licence link has changed is not relivant.
6255  *
6256  * Fork - LGPL
6257  * <script type="text/javascript">
6258  */
6259
6260 /**
6261  * @class Roo.Button
6262  * @extends Roo.util.Observable
6263  * Simple Button class
6264  * @cfg {String} text The button text
6265  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6266  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6267  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6268  * @cfg {Object} scope The scope of the handler
6269  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6270  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6271  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6272  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6273  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6274  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6275    applies if enableToggle = true)
6276  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6277  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6278   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6279  * @constructor
6280  * Create a new button
6281  * @param {Object} config The config object
6282  */
6283 Roo.Button = function(renderTo, config)
6284 {
6285     if (!config) {
6286         config = renderTo;
6287         renderTo = config.renderTo || false;
6288     }
6289     
6290     Roo.apply(this, config);
6291     this.addEvents({
6292         /**
6293              * @event click
6294              * Fires when this button is clicked
6295              * @param {Button} this
6296              * @param {EventObject} e The click event
6297              */
6298             "click" : true,
6299         /**
6300              * @event toggle
6301              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6302              * @param {Button} this
6303              * @param {Boolean} pressed
6304              */
6305             "toggle" : true,
6306         /**
6307              * @event mouseover
6308              * Fires when the mouse hovers over the button
6309              * @param {Button} this
6310              * @param {Event} e The event object
6311              */
6312         'mouseover' : true,
6313         /**
6314              * @event mouseout
6315              * Fires when the mouse exits the button
6316              * @param {Button} this
6317              * @param {Event} e The event object
6318              */
6319         'mouseout': true,
6320          /**
6321              * @event render
6322              * Fires when the button is rendered
6323              * @param {Button} this
6324              */
6325         'render': true
6326     });
6327     if(this.menu){
6328         this.menu = Roo.menu.MenuMgr.get(this.menu);
6329     }
6330     // register listeners first!!  - so render can be captured..
6331     Roo.util.Observable.call(this);
6332     if(renderTo){
6333         this.render(renderTo);
6334     }
6335     
6336   
6337 };
6338
6339 Roo.extend(Roo.Button, Roo.util.Observable, {
6340     /**
6341      * 
6342      */
6343     
6344     /**
6345      * Read-only. True if this button is hidden
6346      * @type Boolean
6347      */
6348     hidden : false,
6349     /**
6350      * Read-only. True if this button is disabled
6351      * @type Boolean
6352      */
6353     disabled : false,
6354     /**
6355      * Read-only. True if this button is pressed (only if enableToggle = true)
6356      * @type Boolean
6357      */
6358     pressed : false,
6359
6360     /**
6361      * @cfg {Number} tabIndex 
6362      * The DOM tabIndex for this button (defaults to undefined)
6363      */
6364     tabIndex : undefined,
6365
6366     /**
6367      * @cfg {Boolean} enableToggle
6368      * True to enable pressed/not pressed toggling (defaults to false)
6369      */
6370     enableToggle: false,
6371     /**
6372      * @cfg {Roo.menu.Menu} menu
6373      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6374      */
6375     menu : undefined,
6376     /**
6377      * @cfg {String} menuAlign
6378      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6379      */
6380     menuAlign : "tl-bl?",
6381
6382     /**
6383      * @cfg {String} iconCls
6384      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6385      */
6386     iconCls : undefined,
6387     /**
6388      * @cfg {String} type
6389      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6390      */
6391     type : 'button',
6392
6393     // private
6394     menuClassTarget: 'tr',
6395
6396     /**
6397      * @cfg {String} clickEvent
6398      * The type of event to map to the button's event handler (defaults to 'click')
6399      */
6400     clickEvent : 'click',
6401
6402     /**
6403      * @cfg {Boolean} handleMouseEvents
6404      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6405      */
6406     handleMouseEvents : true,
6407
6408     /**
6409      * @cfg {String} tooltipType
6410      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6411      */
6412     tooltipType : 'qtip',
6413
6414     /**
6415      * @cfg {String} cls
6416      * A CSS class to apply to the button's main element.
6417      */
6418     
6419     /**
6420      * @cfg {Roo.Template} template (Optional)
6421      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6422      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6423      * require code modifications if required elements (e.g. a button) aren't present.
6424      */
6425
6426     // private
6427     render : function(renderTo){
6428         var btn;
6429         if(this.hideParent){
6430             this.parentEl = Roo.get(renderTo);
6431         }
6432         if(!this.dhconfig){
6433             if(!this.template){
6434                 if(!Roo.Button.buttonTemplate){
6435                     // hideous table template
6436                     Roo.Button.buttonTemplate = new Roo.Template(
6437                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6438                         '<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>',
6439                         "</tr></tbody></table>");
6440                 }
6441                 this.template = Roo.Button.buttonTemplate;
6442             }
6443             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6444             var btnEl = btn.child("button:first");
6445             btnEl.on('focus', this.onFocus, this);
6446             btnEl.on('blur', this.onBlur, this);
6447             if(this.cls){
6448                 btn.addClass(this.cls);
6449             }
6450             if(this.icon){
6451                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6452             }
6453             if(this.iconCls){
6454                 btnEl.addClass(this.iconCls);
6455                 if(!this.cls){
6456                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6457                 }
6458             }
6459             if(this.tabIndex !== undefined){
6460                 btnEl.dom.tabIndex = this.tabIndex;
6461             }
6462             if(this.tooltip){
6463                 if(typeof this.tooltip == 'object'){
6464                     Roo.QuickTips.tips(Roo.apply({
6465                           target: btnEl.id
6466                     }, this.tooltip));
6467                 } else {
6468                     btnEl.dom[this.tooltipType] = this.tooltip;
6469                 }
6470             }
6471         }else{
6472             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6473         }
6474         this.el = btn;
6475         if(this.id){
6476             this.el.dom.id = this.el.id = this.id;
6477         }
6478         if(this.menu){
6479             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6480             this.menu.on("show", this.onMenuShow, this);
6481             this.menu.on("hide", this.onMenuHide, this);
6482         }
6483         btn.addClass("x-btn");
6484         if(Roo.isIE && !Roo.isIE7){
6485             this.autoWidth.defer(1, this);
6486         }else{
6487             this.autoWidth();
6488         }
6489         if(this.handleMouseEvents){
6490             btn.on("mouseover", this.onMouseOver, this);
6491             btn.on("mouseout", this.onMouseOut, this);
6492             btn.on("mousedown", this.onMouseDown, this);
6493         }
6494         btn.on(this.clickEvent, this.onClick, this);
6495         //btn.on("mouseup", this.onMouseUp, this);
6496         if(this.hidden){
6497             this.hide();
6498         }
6499         if(this.disabled){
6500             this.disable();
6501         }
6502         Roo.ButtonToggleMgr.register(this);
6503         if(this.pressed){
6504             this.el.addClass("x-btn-pressed");
6505         }
6506         if(this.repeat){
6507             var repeater = new Roo.util.ClickRepeater(btn,
6508                 typeof this.repeat == "object" ? this.repeat : {}
6509             );
6510             repeater.on("click", this.onClick,  this);
6511         }
6512         
6513         this.fireEvent('render', this);
6514         
6515     },
6516     /**
6517      * Returns the button's underlying element
6518      * @return {Roo.Element} The element
6519      */
6520     getEl : function(){
6521         return this.el;  
6522     },
6523     
6524     /**
6525      * Destroys this Button and removes any listeners.
6526      */
6527     destroy : function(){
6528         Roo.ButtonToggleMgr.unregister(this);
6529         this.el.removeAllListeners();
6530         this.purgeListeners();
6531         this.el.remove();
6532     },
6533
6534     // private
6535     autoWidth : function(){
6536         if(this.el){
6537             this.el.setWidth("auto");
6538             if(Roo.isIE7 && Roo.isStrict){
6539                 var ib = this.el.child('button');
6540                 if(ib && ib.getWidth() > 20){
6541                     ib.clip();
6542                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6543                 }
6544             }
6545             if(this.minWidth){
6546                 if(this.hidden){
6547                     this.el.beginMeasure();
6548                 }
6549                 if(this.el.getWidth() < this.minWidth){
6550                     this.el.setWidth(this.minWidth);
6551                 }
6552                 if(this.hidden){
6553                     this.el.endMeasure();
6554                 }
6555             }
6556         }
6557     },
6558
6559     /**
6560      * Assigns this button's click handler
6561      * @param {Function} handler The function to call when the button is clicked
6562      * @param {Object} scope (optional) Scope for the function passed in
6563      */
6564     setHandler : function(handler, scope){
6565         this.handler = handler;
6566         this.scope = scope;  
6567     },
6568     
6569     /**
6570      * Sets this button's text
6571      * @param {String} text The button text
6572      */
6573     setText : function(text){
6574         this.text = text;
6575         if(this.el){
6576             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6577         }
6578         this.autoWidth();
6579     },
6580     
6581     /**
6582      * Gets the text for this button
6583      * @return {String} The button text
6584      */
6585     getText : function(){
6586         return this.text;  
6587     },
6588     
6589     /**
6590      * Show this button
6591      */
6592     show: function(){
6593         this.hidden = false;
6594         if(this.el){
6595             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6596         }
6597     },
6598     
6599     /**
6600      * Hide this button
6601      */
6602     hide: function(){
6603         this.hidden = true;
6604         if(this.el){
6605             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6606         }
6607     },
6608     
6609     /**
6610      * Convenience function for boolean show/hide
6611      * @param {Boolean} visible True to show, false to hide
6612      */
6613     setVisible: function(visible){
6614         if(visible) {
6615             this.show();
6616         }else{
6617             this.hide();
6618         }
6619     },
6620     
6621     /**
6622      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6623      * @param {Boolean} state (optional) Force a particular state
6624      */
6625     toggle : function(state){
6626         state = state === undefined ? !this.pressed : state;
6627         if(state != this.pressed){
6628             if(state){
6629                 this.el.addClass("x-btn-pressed");
6630                 this.pressed = true;
6631                 this.fireEvent("toggle", this, true);
6632             }else{
6633                 this.el.removeClass("x-btn-pressed");
6634                 this.pressed = false;
6635                 this.fireEvent("toggle", this, false);
6636             }
6637             if(this.toggleHandler){
6638                 this.toggleHandler.call(this.scope || this, this, state);
6639             }
6640         }
6641     },
6642     
6643     /**
6644      * Focus the button
6645      */
6646     focus : function(){
6647         this.el.child('button:first').focus();
6648     },
6649     
6650     /**
6651      * Disable this button
6652      */
6653     disable : function(){
6654         if(this.el){
6655             this.el.addClass("x-btn-disabled");
6656         }
6657         this.disabled = true;
6658     },
6659     
6660     /**
6661      * Enable this button
6662      */
6663     enable : function(){
6664         if(this.el){
6665             this.el.removeClass("x-btn-disabled");
6666         }
6667         this.disabled = false;
6668     },
6669
6670     /**
6671      * Convenience function for boolean enable/disable
6672      * @param {Boolean} enabled True to enable, false to disable
6673      */
6674     setDisabled : function(v){
6675         this[v !== true ? "enable" : "disable"]();
6676     },
6677
6678     // private
6679     onClick : function(e)
6680     {
6681         if(e){
6682             e.preventDefault();
6683         }
6684         if(e.button != 0){
6685             return;
6686         }
6687         if(!this.disabled){
6688             if(this.enableToggle){
6689                 this.toggle();
6690             }
6691             if(this.menu && !this.menu.isVisible()){
6692                 this.menu.show(this.el, this.menuAlign);
6693             }
6694             this.fireEvent("click", this, e);
6695             if(this.handler){
6696                 this.el.removeClass("x-btn-over");
6697                 this.handler.call(this.scope || this, this, e);
6698             }
6699         }
6700     },
6701     // private
6702     onMouseOver : function(e){
6703         if(!this.disabled){
6704             this.el.addClass("x-btn-over");
6705             this.fireEvent('mouseover', this, e);
6706         }
6707     },
6708     // private
6709     onMouseOut : function(e){
6710         if(!e.within(this.el,  true)){
6711             this.el.removeClass("x-btn-over");
6712             this.fireEvent('mouseout', this, e);
6713         }
6714     },
6715     // private
6716     onFocus : function(e){
6717         if(!this.disabled){
6718             this.el.addClass("x-btn-focus");
6719         }
6720     },
6721     // private
6722     onBlur : function(e){
6723         this.el.removeClass("x-btn-focus");
6724     },
6725     // private
6726     onMouseDown : function(e){
6727         if(!this.disabled && e.button == 0){
6728             this.el.addClass("x-btn-click");
6729             Roo.get(document).on('mouseup', this.onMouseUp, this);
6730         }
6731     },
6732     // private
6733     onMouseUp : function(e){
6734         if(e.button == 0){
6735             this.el.removeClass("x-btn-click");
6736             Roo.get(document).un('mouseup', this.onMouseUp, this);
6737         }
6738     },
6739     // private
6740     onMenuShow : function(e){
6741         this.el.addClass("x-btn-menu-active");
6742     },
6743     // private
6744     onMenuHide : function(e){
6745         this.el.removeClass("x-btn-menu-active");
6746     }   
6747 });
6748
6749 // Private utility class used by Button
6750 Roo.ButtonToggleMgr = function(){
6751    var groups = {};
6752    
6753    function toggleGroup(btn, state){
6754        if(state){
6755            var g = groups[btn.toggleGroup];
6756            for(var i = 0, l = g.length; i < l; i++){
6757                if(g[i] != btn){
6758                    g[i].toggle(false);
6759                }
6760            }
6761        }
6762    }
6763    
6764    return {
6765        register : function(btn){
6766            if(!btn.toggleGroup){
6767                return;
6768            }
6769            var g = groups[btn.toggleGroup];
6770            if(!g){
6771                g = groups[btn.toggleGroup] = [];
6772            }
6773            g.push(btn);
6774            btn.on("toggle", toggleGroup);
6775        },
6776        
6777        unregister : function(btn){
6778            if(!btn.toggleGroup){
6779                return;
6780            }
6781            var g = groups[btn.toggleGroup];
6782            if(g){
6783                g.remove(btn);
6784                btn.un("toggle", toggleGroup);
6785            }
6786        }
6787    };
6788 }();/*
6789  * Based on:
6790  * Ext JS Library 1.1.1
6791  * Copyright(c) 2006-2007, Ext JS, LLC.
6792  *
6793  * Originally Released Under LGPL - original licence link has changed is not relivant.
6794  *
6795  * Fork - LGPL
6796  * <script type="text/javascript">
6797  */
6798  
6799 /**
6800  * @class Roo.SplitButton
6801  * @extends Roo.Button
6802  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6803  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6804  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6805  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6806  * @cfg {String} arrowTooltip The title attribute of the arrow
6807  * @constructor
6808  * Create a new menu button
6809  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6810  * @param {Object} config The config object
6811  */
6812 Roo.SplitButton = function(renderTo, config){
6813     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6814     /**
6815      * @event arrowclick
6816      * Fires when this button's arrow is clicked
6817      * @param {SplitButton} this
6818      * @param {EventObject} e The click event
6819      */
6820     this.addEvents({"arrowclick":true});
6821 };
6822
6823 Roo.extend(Roo.SplitButton, Roo.Button, {
6824     render : function(renderTo){
6825         // this is one sweet looking template!
6826         var tpl = new Roo.Template(
6827             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6828             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6829             '<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>',
6830             "</tbody></table></td><td>",
6831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6832             '<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>',
6833             "</tbody></table></td></tr></table>"
6834         );
6835         var btn = tpl.append(renderTo, [this.text, this.type], true);
6836         var btnEl = btn.child("button");
6837         if(this.cls){
6838             btn.addClass(this.cls);
6839         }
6840         if(this.icon){
6841             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6842         }
6843         if(this.iconCls){
6844             btnEl.addClass(this.iconCls);
6845             if(!this.cls){
6846                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6847             }
6848         }
6849         this.el = btn;
6850         if(this.handleMouseEvents){
6851             btn.on("mouseover", this.onMouseOver, this);
6852             btn.on("mouseout", this.onMouseOut, this);
6853             btn.on("mousedown", this.onMouseDown, this);
6854             btn.on("mouseup", this.onMouseUp, this);
6855         }
6856         btn.on(this.clickEvent, this.onClick, this);
6857         if(this.tooltip){
6858             if(typeof this.tooltip == 'object'){
6859                 Roo.QuickTips.tips(Roo.apply({
6860                       target: btnEl.id
6861                 }, this.tooltip));
6862             } else {
6863                 btnEl.dom[this.tooltipType] = this.tooltip;
6864             }
6865         }
6866         if(this.arrowTooltip){
6867             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6868         }
6869         if(this.hidden){
6870             this.hide();
6871         }
6872         if(this.disabled){
6873             this.disable();
6874         }
6875         if(this.pressed){
6876             this.el.addClass("x-btn-pressed");
6877         }
6878         if(Roo.isIE && !Roo.isIE7){
6879             this.autoWidth.defer(1, this);
6880         }else{
6881             this.autoWidth();
6882         }
6883         if(this.menu){
6884             this.menu.on("show", this.onMenuShow, this);
6885             this.menu.on("hide", this.onMenuHide, this);
6886         }
6887         this.fireEvent('render', this);
6888     },
6889
6890     // private
6891     autoWidth : function(){
6892         if(this.el){
6893             var tbl = this.el.child("table:first");
6894             var tbl2 = this.el.child("table:last");
6895             this.el.setWidth("auto");
6896             tbl.setWidth("auto");
6897             if(Roo.isIE7 && Roo.isStrict){
6898                 var ib = this.el.child('button:first');
6899                 if(ib && ib.getWidth() > 20){
6900                     ib.clip();
6901                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6902                 }
6903             }
6904             if(this.minWidth){
6905                 if(this.hidden){
6906                     this.el.beginMeasure();
6907                 }
6908                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6909                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6910                 }
6911                 if(this.hidden){
6912                     this.el.endMeasure();
6913                 }
6914             }
6915             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6916         } 
6917     },
6918     /**
6919      * Sets this button's click handler
6920      * @param {Function} handler The function to call when the button is clicked
6921      * @param {Object} scope (optional) Scope for the function passed above
6922      */
6923     setHandler : function(handler, scope){
6924         this.handler = handler;
6925         this.scope = scope;  
6926     },
6927     
6928     /**
6929      * Sets this button's arrow click handler
6930      * @param {Function} handler The function to call when the arrow is clicked
6931      * @param {Object} scope (optional) Scope for the function passed above
6932      */
6933     setArrowHandler : function(handler, scope){
6934         this.arrowHandler = handler;
6935         this.scope = scope;  
6936     },
6937     
6938     /**
6939      * Focus the button
6940      */
6941     focus : function(){
6942         if(this.el){
6943             this.el.child("button:first").focus();
6944         }
6945     },
6946
6947     // private
6948     onClick : function(e){
6949         e.preventDefault();
6950         if(!this.disabled){
6951             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6952                 if(this.menu && !this.menu.isVisible()){
6953                     this.menu.show(this.el, this.menuAlign);
6954                 }
6955                 this.fireEvent("arrowclick", this, e);
6956                 if(this.arrowHandler){
6957                     this.arrowHandler.call(this.scope || this, this, e);
6958                 }
6959             }else{
6960                 this.fireEvent("click", this, e);
6961                 if(this.handler){
6962                     this.handler.call(this.scope || this, this, e);
6963                 }
6964             }
6965         }
6966     },
6967     // private
6968     onMouseDown : function(e){
6969         if(!this.disabled){
6970             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6971         }
6972     },
6973     // private
6974     onMouseUp : function(e){
6975         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6976     }   
6977 });
6978
6979
6980 // backwards compat
6981 Roo.MenuButton = Roo.SplitButton;/*
6982  * Based on:
6983  * Ext JS Library 1.1.1
6984  * Copyright(c) 2006-2007, Ext JS, LLC.
6985  *
6986  * Originally Released Under LGPL - original licence link has changed is not relivant.
6987  *
6988  * Fork - LGPL
6989  * <script type="text/javascript">
6990  */
6991
6992 /**
6993  * @class Roo.Toolbar
6994  * @children   Roo.Toolbar.Item Roo.form.Field
6995  * Basic Toolbar class.
6996  * @constructor
6997  * Creates a new Toolbar
6998  * @param {Object} container The config object
6999  */ 
7000 Roo.Toolbar = function(container, buttons, config)
7001 {
7002     /// old consturctor format still supported..
7003     if(container instanceof Array){ // omit the container for later rendering
7004         buttons = container;
7005         config = buttons;
7006         container = null;
7007     }
7008     if (typeof(container) == 'object' && container.xtype) {
7009         config = container;
7010         container = config.container;
7011         buttons = config.buttons || []; // not really - use items!!
7012     }
7013     var xitems = [];
7014     if (config && config.items) {
7015         xitems = config.items;
7016         delete config.items;
7017     }
7018     Roo.apply(this, config);
7019     this.buttons = buttons;
7020     
7021     if(container){
7022         this.render(container);
7023     }
7024     this.xitems = xitems;
7025     Roo.each(xitems, function(b) {
7026         this.add(b);
7027     }, this);
7028     
7029 };
7030
7031 Roo.Toolbar.prototype = {
7032     /**
7033      * @cfg {Array} items
7034      * array of button configs or elements to add (will be converted to a MixedCollection)
7035      */
7036     items: false,
7037     /**
7038      * @cfg {String/HTMLElement/Element} container
7039      * The id or element that will contain the toolbar
7040      */
7041     // private
7042     render : function(ct){
7043         this.el = Roo.get(ct);
7044         if(this.cls){
7045             this.el.addClass(this.cls);
7046         }
7047         // using a table allows for vertical alignment
7048         // 100% width is needed by Safari...
7049         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7050         this.tr = this.el.child("tr", true);
7051         var autoId = 0;
7052         this.items = new Roo.util.MixedCollection(false, function(o){
7053             return o.id || ("item" + (++autoId));
7054         });
7055         if(this.buttons){
7056             this.add.apply(this, this.buttons);
7057             delete this.buttons;
7058         }
7059     },
7060
7061     /**
7062      * Adds element(s) to the toolbar -- this function takes a variable number of 
7063      * arguments of mixed type and adds them to the toolbar.
7064      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7065      * <ul>
7066      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7067      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7068      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7069      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7070      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7071      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7072      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7073      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7074      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7075      * </ul>
7076      * @param {Mixed} arg2
7077      * @param {Mixed} etc.
7078      */
7079     add : function(){
7080         var a = arguments, l = a.length;
7081         for(var i = 0; i < l; i++){
7082             this._add(a[i]);
7083         }
7084     },
7085     // private..
7086     _add : function(el) {
7087         
7088         if (el.xtype) {
7089             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7090         }
7091         
7092         if (el.applyTo){ // some kind of form field
7093             return this.addField(el);
7094         } 
7095         if (el.render){ // some kind of Toolbar.Item
7096             return this.addItem(el);
7097         }
7098         if (typeof el == "string"){ // string
7099             if(el == "separator" || el == "-"){
7100                 return this.addSeparator();
7101             }
7102             if (el == " "){
7103                 return this.addSpacer();
7104             }
7105             if(el == "->"){
7106                 return this.addFill();
7107             }
7108             return this.addText(el);
7109             
7110         }
7111         if(el.tagName){ // element
7112             return this.addElement(el);
7113         }
7114         if(typeof el == "object"){ // must be button config?
7115             return this.addButton(el);
7116         }
7117         // and now what?!?!
7118         return false;
7119         
7120     },
7121     
7122     /**
7123      * Add an Xtype element
7124      * @param {Object} xtype Xtype Object
7125      * @return {Object} created Object
7126      */
7127     addxtype : function(e){
7128         return this.add(e);  
7129     },
7130     
7131     /**
7132      * Returns the Element for this toolbar.
7133      * @return {Roo.Element}
7134      */
7135     getEl : function(){
7136         return this.el;  
7137     },
7138     
7139     /**
7140      * Adds a separator
7141      * @return {Roo.Toolbar.Item} The separator item
7142      */
7143     addSeparator : function(){
7144         return this.addItem(new Roo.Toolbar.Separator());
7145     },
7146
7147     /**
7148      * Adds a spacer element
7149      * @return {Roo.Toolbar.Spacer} The spacer item
7150      */
7151     addSpacer : function(){
7152         return this.addItem(new Roo.Toolbar.Spacer());
7153     },
7154
7155     /**
7156      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7157      * @return {Roo.Toolbar.Fill} The fill item
7158      */
7159     addFill : function(){
7160         return this.addItem(new Roo.Toolbar.Fill());
7161     },
7162
7163     /**
7164      * Adds any standard HTML element to the toolbar
7165      * @param {String/HTMLElement/Element} el The element or id of the element to add
7166      * @return {Roo.Toolbar.Item} The element's item
7167      */
7168     addElement : function(el){
7169         return this.addItem(new Roo.Toolbar.Item(el));
7170     },
7171     /**
7172      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7173      * @type Roo.util.MixedCollection  
7174      */
7175     items : false,
7176      
7177     /**
7178      * Adds any Toolbar.Item or subclass
7179      * @param {Roo.Toolbar.Item} item
7180      * @return {Roo.Toolbar.Item} The item
7181      */
7182     addItem : function(item){
7183         var td = this.nextBlock();
7184         item.render(td);
7185         this.items.add(item);
7186         return item;
7187     },
7188     
7189     /**
7190      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7191      * @param {Object/Array} config A button config or array of configs
7192      * @return {Roo.Toolbar.Button/Array}
7193      */
7194     addButton : function(config){
7195         if(config instanceof Array){
7196             var buttons = [];
7197             for(var i = 0, len = config.length; i < len; i++) {
7198                 buttons.push(this.addButton(config[i]));
7199             }
7200             return buttons;
7201         }
7202         var b = config;
7203         if(!(config instanceof Roo.Toolbar.Button)){
7204             b = config.split ?
7205                 new Roo.Toolbar.SplitButton(config) :
7206                 new Roo.Toolbar.Button(config);
7207         }
7208         var td = this.nextBlock();
7209         b.render(td);
7210         this.items.add(b);
7211         return b;
7212     },
7213     
7214     /**
7215      * Adds text to the toolbar
7216      * @param {String} text The text to add
7217      * @return {Roo.Toolbar.Item} The element's item
7218      */
7219     addText : function(text){
7220         return this.addItem(new Roo.Toolbar.TextItem(text));
7221     },
7222     
7223     /**
7224      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7225      * @param {Number} index The index where the item is to be inserted
7226      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7227      * @return {Roo.Toolbar.Button/Item}
7228      */
7229     insertButton : function(index, item){
7230         if(item instanceof Array){
7231             var buttons = [];
7232             for(var i = 0, len = item.length; i < len; i++) {
7233                buttons.push(this.insertButton(index + i, item[i]));
7234             }
7235             return buttons;
7236         }
7237         if (!(item instanceof Roo.Toolbar.Button)){
7238            item = new Roo.Toolbar.Button(item);
7239         }
7240         var td = document.createElement("td");
7241         this.tr.insertBefore(td, this.tr.childNodes[index]);
7242         item.render(td);
7243         this.items.insert(index, item);
7244         return item;
7245     },
7246     
7247     /**
7248      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7249      * @param {Object} config
7250      * @return {Roo.Toolbar.Item} The element's item
7251      */
7252     addDom : function(config, returnEl){
7253         var td = this.nextBlock();
7254         Roo.DomHelper.overwrite(td, config);
7255         var ti = new Roo.Toolbar.Item(td.firstChild);
7256         ti.render(td);
7257         this.items.add(ti);
7258         return ti;
7259     },
7260
7261     /**
7262      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7263      * @type Roo.util.MixedCollection  
7264      */
7265     fields : false,
7266     
7267     /**
7268      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7269      * Note: the field should not have been rendered yet. For a field that has already been
7270      * rendered, use {@link #addElement}.
7271      * @param {Roo.form.Field} field
7272      * @return {Roo.ToolbarItem}
7273      */
7274      
7275       
7276     addField : function(field) {
7277         if (!this.fields) {
7278             var autoId = 0;
7279             this.fields = new Roo.util.MixedCollection(false, function(o){
7280                 return o.id || ("item" + (++autoId));
7281             });
7282
7283         }
7284         
7285         var td = this.nextBlock();
7286         field.render(td);
7287         var ti = new Roo.Toolbar.Item(td.firstChild);
7288         ti.render(td);
7289         this.items.add(ti);
7290         this.fields.add(field);
7291         return ti;
7292     },
7293     /**
7294      * Hide the toolbar
7295      * @method hide
7296      */
7297      
7298       
7299     hide : function()
7300     {
7301         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7302         this.el.child('div').hide();
7303     },
7304     /**
7305      * Show the toolbar
7306      * @method show
7307      */
7308     show : function()
7309     {
7310         this.el.child('div').show();
7311     },
7312       
7313     // private
7314     nextBlock : function(){
7315         var td = document.createElement("td");
7316         this.tr.appendChild(td);
7317         return td;
7318     },
7319
7320     // private
7321     destroy : function(){
7322         if(this.items){ // rendered?
7323             Roo.destroy.apply(Roo, this.items.items);
7324         }
7325         if(this.fields){ // rendered?
7326             Roo.destroy.apply(Roo, this.fields.items);
7327         }
7328         Roo.Element.uncache(this.el, this.tr);
7329     }
7330 };
7331
7332 /**
7333  * @class Roo.Toolbar.Item
7334  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7335  * @constructor
7336  * Creates a new Item
7337  * @param {HTMLElement} el 
7338  */
7339 Roo.Toolbar.Item = function(el){
7340     var cfg = {};
7341     if (typeof (el.xtype) != 'undefined') {
7342         cfg = el;
7343         el = cfg.el;
7344     }
7345     
7346     this.el = Roo.getDom(el);
7347     this.id = Roo.id(this.el);
7348     this.hidden = false;
7349     
7350     this.addEvents({
7351          /**
7352              * @event render
7353              * Fires when the button is rendered
7354              * @param {Button} this
7355              */
7356         'render': true
7357     });
7358     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7359 };
7360 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7361 //Roo.Toolbar.Item.prototype = {
7362     
7363     /**
7364      * Get this item's HTML Element
7365      * @return {HTMLElement}
7366      */
7367     getEl : function(){
7368        return this.el;  
7369     },
7370
7371     // private
7372     render : function(td){
7373         
7374          this.td = td;
7375         td.appendChild(this.el);
7376         
7377         this.fireEvent('render', this);
7378     },
7379     
7380     /**
7381      * Removes and destroys this item.
7382      */
7383     destroy : function(){
7384         this.td.parentNode.removeChild(this.td);
7385     },
7386     
7387     /**
7388      * Shows this item.
7389      */
7390     show: function(){
7391         this.hidden = false;
7392         this.td.style.display = "";
7393     },
7394     
7395     /**
7396      * Hides this item.
7397      */
7398     hide: function(){
7399         this.hidden = true;
7400         this.td.style.display = "none";
7401     },
7402     
7403     /**
7404      * Convenience function for boolean show/hide.
7405      * @param {Boolean} visible true to show/false to hide
7406      */
7407     setVisible: function(visible){
7408         if(visible) {
7409             this.show();
7410         }else{
7411             this.hide();
7412         }
7413     },
7414     
7415     /**
7416      * Try to focus this item.
7417      */
7418     focus : function(){
7419         Roo.fly(this.el).focus();
7420     },
7421     
7422     /**
7423      * Disables this item.
7424      */
7425     disable : function(){
7426         Roo.fly(this.td).addClass("x-item-disabled");
7427         this.disabled = true;
7428         this.el.disabled = true;
7429     },
7430     
7431     /**
7432      * Enables this item.
7433      */
7434     enable : function(){
7435         Roo.fly(this.td).removeClass("x-item-disabled");
7436         this.disabled = false;
7437         this.el.disabled = false;
7438     }
7439 });
7440
7441
7442 /**
7443  * @class Roo.Toolbar.Separator
7444  * @extends Roo.Toolbar.Item
7445  * A simple toolbar separator class
7446  * @constructor
7447  * Creates a new Separator
7448  */
7449 Roo.Toolbar.Separator = function(cfg){
7450     
7451     var s = document.createElement("span");
7452     s.className = "ytb-sep";
7453     if (cfg) {
7454         cfg.el = s;
7455     }
7456     
7457     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7458 };
7459 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7460     enable:Roo.emptyFn,
7461     disable:Roo.emptyFn,
7462     focus:Roo.emptyFn
7463 });
7464
7465 /**
7466  * @class Roo.Toolbar.Spacer
7467  * @extends Roo.Toolbar.Item
7468  * A simple element that adds extra horizontal space to a toolbar.
7469  * @constructor
7470  * Creates a new Spacer
7471  */
7472 Roo.Toolbar.Spacer = function(cfg){
7473     var s = document.createElement("div");
7474     s.className = "ytb-spacer";
7475     if (cfg) {
7476         cfg.el = s;
7477     }
7478     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7479 };
7480 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7481     enable:Roo.emptyFn,
7482     disable:Roo.emptyFn,
7483     focus:Roo.emptyFn
7484 });
7485
7486 /**
7487  * @class Roo.Toolbar.Fill
7488  * @extends Roo.Toolbar.Spacer
7489  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7490  * @constructor
7491  * Creates a new Spacer
7492  */
7493 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7494     // private
7495     render : function(td){
7496         td.style.width = '100%';
7497         Roo.Toolbar.Fill.superclass.render.call(this, td);
7498     }
7499 });
7500
7501 /**
7502  * @class Roo.Toolbar.TextItem
7503  * @extends Roo.Toolbar.Item
7504  * A simple class that renders text directly into a toolbar.
7505  * @constructor
7506  * Creates a new TextItem
7507  * @cfg {string} text 
7508  */
7509 Roo.Toolbar.TextItem = function(cfg){
7510     var  text = cfg || "";
7511     if (typeof(cfg) == 'object') {
7512         text = cfg.text || "";
7513     }  else {
7514         cfg = null;
7515     }
7516     var s = document.createElement("span");
7517     s.className = "ytb-text";
7518     s.innerHTML = text;
7519     if (cfg) {
7520         cfg.el  = s;
7521     }
7522     
7523     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7524 };
7525 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7526     
7527      
7528     enable:Roo.emptyFn,
7529     disable:Roo.emptyFn,
7530     focus:Roo.emptyFn
7531 });
7532
7533 /**
7534  * @class Roo.Toolbar.Button
7535  * @extends Roo.Button
7536  * A button that renders into a toolbar.
7537  * @constructor
7538  * Creates a new Button
7539  * @param {Object} config A standard {@link Roo.Button} config object
7540  */
7541 Roo.Toolbar.Button = function(config){
7542     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7543 };
7544 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7545 {
7546     
7547     
7548     render : function(td){
7549         this.td = td;
7550         Roo.Toolbar.Button.superclass.render.call(this, td);
7551     },
7552     
7553     /**
7554      * Removes and destroys this button
7555      */
7556     destroy : function(){
7557         Roo.Toolbar.Button.superclass.destroy.call(this);
7558         this.td.parentNode.removeChild(this.td);
7559     },
7560     
7561     /**
7562      * Shows this button
7563      */
7564     show: function(){
7565         this.hidden = false;
7566         this.td.style.display = "";
7567     },
7568     
7569     /**
7570      * Hides this button
7571      */
7572     hide: function(){
7573         this.hidden = true;
7574         this.td.style.display = "none";
7575     },
7576
7577     /**
7578      * Disables this item
7579      */
7580     disable : function(){
7581         Roo.fly(this.td).addClass("x-item-disabled");
7582         this.disabled = true;
7583     },
7584
7585     /**
7586      * Enables this item
7587      */
7588     enable : function(){
7589         Roo.fly(this.td).removeClass("x-item-disabled");
7590         this.disabled = false;
7591     }
7592 });
7593 // backwards compat
7594 Roo.ToolbarButton = Roo.Toolbar.Button;
7595
7596 /**
7597  * @class Roo.Toolbar.SplitButton
7598  * @extends Roo.SplitButton
7599  * A menu button that renders into a toolbar.
7600  * @constructor
7601  * Creates a new SplitButton
7602  * @param {Object} config A standard {@link Roo.SplitButton} config object
7603  */
7604 Roo.Toolbar.SplitButton = function(config){
7605     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7606 };
7607 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7608     render : function(td){
7609         this.td = td;
7610         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7611     },
7612     
7613     /**
7614      * Removes and destroys this button
7615      */
7616     destroy : function(){
7617         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7618         this.td.parentNode.removeChild(this.td);
7619     },
7620     
7621     /**
7622      * Shows this button
7623      */
7624     show: function(){
7625         this.hidden = false;
7626         this.td.style.display = "";
7627     },
7628     
7629     /**
7630      * Hides this button
7631      */
7632     hide: function(){
7633         this.hidden = true;
7634         this.td.style.display = "none";
7635     }
7636 });
7637
7638 // backwards compat
7639 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7640  * Based on:
7641  * Ext JS Library 1.1.1
7642  * Copyright(c) 2006-2007, Ext JS, LLC.
7643  *
7644  * Originally Released Under LGPL - original licence link has changed is not relivant.
7645  *
7646  * Fork - LGPL
7647  * <script type="text/javascript">
7648  */
7649  
7650 /**
7651  * @class Roo.PagingToolbar
7652  * @extends Roo.Toolbar
7653  * @children   Roo.Toolbar.Item Roo.form.Field
7654  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7655  * @constructor
7656  * Create a new PagingToolbar
7657  * @param {Object} config The config object
7658  */
7659 Roo.PagingToolbar = function(el, ds, config)
7660 {
7661     // old args format still supported... - xtype is prefered..
7662     if (typeof(el) == 'object' && el.xtype) {
7663         // created from xtype...
7664         config = el;
7665         ds = el.dataSource;
7666         el = config.container;
7667     }
7668     var items = [];
7669     if (config.items) {
7670         items = config.items;
7671         config.items = [];
7672     }
7673     
7674     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7675     this.ds = ds;
7676     this.cursor = 0;
7677     this.renderButtons(this.el);
7678     this.bind(ds);
7679     
7680     // supprot items array.
7681    
7682     Roo.each(items, function(e) {
7683         this.add(Roo.factory(e));
7684     },this);
7685     
7686 };
7687
7688 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7689     /**
7690      * @cfg {Roo.data.Store} dataSource
7691      * The underlying data store providing the paged data
7692      */
7693     /**
7694      * @cfg {String/HTMLElement/Element} container
7695      * container The id or element that will contain the toolbar
7696      */
7697     /**
7698      * @cfg {Boolean} displayInfo
7699      * True to display the displayMsg (defaults to false)
7700      */
7701     /**
7702      * @cfg {Number} pageSize
7703      * The number of records to display per page (defaults to 20)
7704      */
7705     pageSize: 20,
7706     /**
7707      * @cfg {String} displayMsg
7708      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7709      */
7710     displayMsg : 'Displaying {0} - {1} of {2}',
7711     /**
7712      * @cfg {String} emptyMsg
7713      * The message to display when no records are found (defaults to "No data to display")
7714      */
7715     emptyMsg : 'No data to display',
7716     /**
7717      * Customizable piece of the default paging text (defaults to "Page")
7718      * @type String
7719      */
7720     beforePageText : "Page",
7721     /**
7722      * Customizable piece of the default paging text (defaults to "of %0")
7723      * @type String
7724      */
7725     afterPageText : "of {0}",
7726     /**
7727      * Customizable piece of the default paging text (defaults to "First Page")
7728      * @type String
7729      */
7730     firstText : "First Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "Previous Page")
7733      * @type String
7734      */
7735     prevText : "Previous Page",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "Next Page")
7738      * @type String
7739      */
7740     nextText : "Next Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Last Page")
7743      * @type String
7744      */
7745     lastText : "Last Page",
7746     /**
7747      * Customizable piece of the default paging text (defaults to "Refresh")
7748      * @type String
7749      */
7750     refreshText : "Refresh",
7751
7752     // private
7753     renderButtons : function(el){
7754         Roo.PagingToolbar.superclass.render.call(this, el);
7755         this.first = this.addButton({
7756             tooltip: this.firstText,
7757             cls: "x-btn-icon x-grid-page-first",
7758             disabled: true,
7759             handler: this.onClick.createDelegate(this, ["first"])
7760         });
7761         this.prev = this.addButton({
7762             tooltip: this.prevText,
7763             cls: "x-btn-icon x-grid-page-prev",
7764             disabled: true,
7765             handler: this.onClick.createDelegate(this, ["prev"])
7766         });
7767         //this.addSeparator();
7768         this.add(this.beforePageText);
7769         this.field = Roo.get(this.addDom({
7770            tag: "input",
7771            type: "text",
7772            size: "3",
7773            value: "1",
7774            cls: "x-grid-page-number"
7775         }).el);
7776         this.field.on("keydown", this.onPagingKeydown, this);
7777         this.field.on("focus", function(){this.dom.select();});
7778         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7779         this.field.setHeight(18);
7780         //this.addSeparator();
7781         this.next = this.addButton({
7782             tooltip: this.nextText,
7783             cls: "x-btn-icon x-grid-page-next",
7784             disabled: true,
7785             handler: this.onClick.createDelegate(this, ["next"])
7786         });
7787         this.last = this.addButton({
7788             tooltip: this.lastText,
7789             cls: "x-btn-icon x-grid-page-last",
7790             disabled: true,
7791             handler: this.onClick.createDelegate(this, ["last"])
7792         });
7793         //this.addSeparator();
7794         this.loading = this.addButton({
7795             tooltip: this.refreshText,
7796             cls: "x-btn-icon x-grid-loading",
7797             handler: this.onClick.createDelegate(this, ["refresh"])
7798         });
7799
7800         if(this.displayInfo){
7801             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7802         }
7803     },
7804
7805     // private
7806     updateInfo : function(){
7807         if(this.displayEl){
7808             var count = this.ds.getCount();
7809             var msg = count == 0 ?
7810                 this.emptyMsg :
7811                 String.format(
7812                     this.displayMsg,
7813                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7814                 );
7815             this.displayEl.update(msg);
7816         }
7817     },
7818
7819     // private
7820     onLoad : function(ds, r, o){
7821        this.cursor = o.params ? o.params.start : 0;
7822        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7823
7824        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7825        this.field.dom.value = ap;
7826        this.first.setDisabled(ap == 1);
7827        this.prev.setDisabled(ap == 1);
7828        this.next.setDisabled(ap == ps);
7829        this.last.setDisabled(ap == ps);
7830        this.loading.enable();
7831        this.updateInfo();
7832     },
7833
7834     // private
7835     getPageData : function(){
7836         var total = this.ds.getTotalCount();
7837         return {
7838             total : total,
7839             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7840             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7841         };
7842     },
7843
7844     // private
7845     onLoadError : function(){
7846         this.loading.enable();
7847     },
7848
7849     // private
7850     onPagingKeydown : function(e){
7851         var k = e.getKey();
7852         var d = this.getPageData();
7853         if(k == e.RETURN){
7854             var v = this.field.dom.value, pageNum;
7855             if(!v || isNaN(pageNum = parseInt(v, 10))){
7856                 this.field.dom.value = d.activePage;
7857                 return;
7858             }
7859             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7861             e.stopEvent();
7862         }
7863         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))
7864         {
7865           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7866           this.field.dom.value = pageNum;
7867           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7868           e.stopEvent();
7869         }
7870         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7871         {
7872           var v = this.field.dom.value, pageNum; 
7873           var increment = (e.shiftKey) ? 10 : 1;
7874           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7875             increment *= -1;
7876           }
7877           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7878             this.field.dom.value = d.activePage;
7879             return;
7880           }
7881           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7882           {
7883             this.field.dom.value = parseInt(v, 10) + increment;
7884             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7885             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7886           }
7887           e.stopEvent();
7888         }
7889     },
7890
7891     // private
7892     beforeLoad : function(){
7893         if(this.loading){
7894             this.loading.disable();
7895         }
7896     },
7897
7898     // private
7899     onClick : function(which){
7900         var ds = this.ds;
7901         switch(which){
7902             case "first":
7903                 ds.load({params:{start: 0, limit: this.pageSize}});
7904             break;
7905             case "prev":
7906                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7907             break;
7908             case "next":
7909                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7910             break;
7911             case "last":
7912                 var total = ds.getTotalCount();
7913                 var extra = total % this.pageSize;
7914                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7915                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7916             break;
7917             case "refresh":
7918                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7919             break;
7920         }
7921     },
7922
7923     /**
7924      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7925      * @param {Roo.data.Store} store The data store to unbind
7926      */
7927     unbind : function(ds){
7928         ds.un("beforeload", this.beforeLoad, this);
7929         ds.un("load", this.onLoad, this);
7930         ds.un("loadexception", this.onLoadError, this);
7931         ds.un("remove", this.updateInfo, this);
7932         ds.un("add", this.updateInfo, this);
7933         this.ds = undefined;
7934     },
7935
7936     /**
7937      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7938      * @param {Roo.data.Store} store The data store to bind
7939      */
7940     bind : function(ds){
7941         ds.on("beforeload", this.beforeLoad, this);
7942         ds.on("load", this.onLoad, this);
7943         ds.on("loadexception", this.onLoadError, this);
7944         ds.on("remove", this.updateInfo, this);
7945         ds.on("add", this.updateInfo, this);
7946         this.ds = ds;
7947     }
7948 });/*
7949  * Based on:
7950  * Ext JS Library 1.1.1
7951  * Copyright(c) 2006-2007, Ext JS, LLC.
7952  *
7953  * Originally Released Under LGPL - original licence link has changed is not relivant.
7954  *
7955  * Fork - LGPL
7956  * <script type="text/javascript">
7957  */
7958
7959 /**
7960  * @class Roo.Resizable
7961  * @extends Roo.util.Observable
7962  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7963  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7964  * 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
7965  * the element will be wrapped for you automatically.</p>
7966  * <p>Here is the list of valid resize handles:</p>
7967  * <pre>
7968 Value   Description
7969 ------  -------------------
7970  'n'     north
7971  's'     south
7972  'e'     east
7973  'w'     west
7974  'nw'    northwest
7975  'sw'    southwest
7976  'se'    southeast
7977  'ne'    northeast
7978  'hd'    horizontal drag
7979  'all'   all
7980 </pre>
7981  * <p>Here's an example showing the creation of a typical Resizable:</p>
7982  * <pre><code>
7983 var resizer = new Roo.Resizable("element-id", {
7984     handles: 'all',
7985     minWidth: 200,
7986     minHeight: 100,
7987     maxWidth: 500,
7988     maxHeight: 400,
7989     pinned: true
7990 });
7991 resizer.on("resize", myHandler);
7992 </code></pre>
7993  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7994  * resizer.east.setDisplayed(false);</p>
7995  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7996  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7997  * resize operation's new size (defaults to [0, 0])
7998  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7999  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8000  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8001  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8002  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8003  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8004  * @cfg {Number} width The width of the element in pixels (defaults to null)
8005  * @cfg {Number} height The height of the element in pixels (defaults to null)
8006  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8007  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8008  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8009  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8010  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8011  * in favor of the handles config option (defaults to false)
8012  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8013  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8014  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8015  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8016  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8017  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8018  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8019  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8020  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8021  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8022  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8023  * @constructor
8024  * Create a new resizable component
8025  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8026  * @param {Object} config configuration options
8027   */
8028 Roo.Resizable = function(el, config)
8029 {
8030     this.el = Roo.get(el);
8031
8032     if(config && config.wrap){
8033         config.resizeChild = this.el;
8034         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8035         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8036         this.el.setStyle("overflow", "hidden");
8037         this.el.setPositioning(config.resizeChild.getPositioning());
8038         config.resizeChild.clearPositioning();
8039         if(!config.width || !config.height){
8040             var csize = config.resizeChild.getSize();
8041             this.el.setSize(csize.width, csize.height);
8042         }
8043         if(config.pinned && !config.adjustments){
8044             config.adjustments = "auto";
8045         }
8046     }
8047
8048     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8049     this.proxy.unselectable();
8050     this.proxy.enableDisplayMode('block');
8051
8052     Roo.apply(this, config);
8053
8054     if(this.pinned){
8055         this.disableTrackOver = true;
8056         this.el.addClass("x-resizable-pinned");
8057     }
8058     // if the element isn't positioned, make it relative
8059     var position = this.el.getStyle("position");
8060     if(position != "absolute" && position != "fixed"){
8061         this.el.setStyle("position", "relative");
8062     }
8063     if(!this.handles){ // no handles passed, must be legacy style
8064         this.handles = 's,e,se';
8065         if(this.multiDirectional){
8066             this.handles += ',n,w';
8067         }
8068     }
8069     if(this.handles == "all"){
8070         this.handles = "n s e w ne nw se sw";
8071     }
8072     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8073     var ps = Roo.Resizable.positions;
8074     for(var i = 0, len = hs.length; i < len; i++){
8075         if(hs[i] && ps[hs[i]]){
8076             var pos = ps[hs[i]];
8077             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8078         }
8079     }
8080     // legacy
8081     this.corner = this.southeast;
8082     
8083     // updateBox = the box can move..
8084     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8085         this.updateBox = true;
8086     }
8087
8088     this.activeHandle = null;
8089
8090     if(this.resizeChild){
8091         if(typeof this.resizeChild == "boolean"){
8092             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8093         }else{
8094             this.resizeChild = Roo.get(this.resizeChild, true);
8095         }
8096     }
8097     
8098     if(this.adjustments == "auto"){
8099         var rc = this.resizeChild;
8100         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8101         if(rc && (hw || hn)){
8102             rc.position("relative");
8103             rc.setLeft(hw ? hw.el.getWidth() : 0);
8104             rc.setTop(hn ? hn.el.getHeight() : 0);
8105         }
8106         this.adjustments = [
8107             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8108             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8109         ];
8110     }
8111
8112     if(this.draggable){
8113         this.dd = this.dynamic ?
8114             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8115         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8116     }
8117
8118     // public events
8119     this.addEvents({
8120         /**
8121          * @event beforeresize
8122          * Fired before resize is allowed. Set enabled to false to cancel resize.
8123          * @param {Roo.Resizable} this
8124          * @param {Roo.EventObject} e The mousedown event
8125          */
8126         "beforeresize" : true,
8127         /**
8128          * @event resizing
8129          * Fired a resizing.
8130          * @param {Roo.Resizable} this
8131          * @param {Number} x The new x position
8132          * @param {Number} y The new y position
8133          * @param {Number} w The new w width
8134          * @param {Number} h The new h hight
8135          * @param {Roo.EventObject} e The mouseup event
8136          */
8137         "resizing" : true,
8138         /**
8139          * @event resize
8140          * Fired after a resize.
8141          * @param {Roo.Resizable} this
8142          * @param {Number} width The new width
8143          * @param {Number} height The new height
8144          * @param {Roo.EventObject} e The mouseup event
8145          */
8146         "resize" : true
8147     });
8148
8149     if(this.width !== null && this.height !== null){
8150         this.resizeTo(this.width, this.height);
8151     }else{
8152         this.updateChildSize();
8153     }
8154     if(Roo.isIE){
8155         this.el.dom.style.zoom = 1;
8156     }
8157     Roo.Resizable.superclass.constructor.call(this);
8158 };
8159
8160 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8161         resizeChild : false,
8162         adjustments : [0, 0],
8163         minWidth : 5,
8164         minHeight : 5,
8165         maxWidth : 10000,
8166         maxHeight : 10000,
8167         enabled : true,
8168         animate : false,
8169         duration : .35,
8170         dynamic : false,
8171         handles : false,
8172         multiDirectional : false,
8173         disableTrackOver : false,
8174         easing : 'easeOutStrong',
8175         widthIncrement : 0,
8176         heightIncrement : 0,
8177         pinned : false,
8178         width : null,
8179         height : null,
8180         preserveRatio : false,
8181         transparent: false,
8182         minX: 0,
8183         minY: 0,
8184         draggable: false,
8185
8186         /**
8187          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8188          */
8189         constrainTo: undefined,
8190         /**
8191          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8192          */
8193         resizeRegion: undefined,
8194
8195
8196     /**
8197      * Perform a manual resize
8198      * @param {Number} width
8199      * @param {Number} height
8200      */
8201     resizeTo : function(width, height){
8202         this.el.setSize(width, height);
8203         this.updateChildSize();
8204         this.fireEvent("resize", this, width, height, null);
8205     },
8206
8207     // private
8208     startSizing : function(e, handle){
8209         this.fireEvent("beforeresize", this, e);
8210         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8211
8212             if(!this.overlay){
8213                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8214                 this.overlay.unselectable();
8215                 this.overlay.enableDisplayMode("block");
8216                 this.overlay.on("mousemove", this.onMouseMove, this);
8217                 this.overlay.on("mouseup", this.onMouseUp, this);
8218             }
8219             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8220
8221             this.resizing = true;
8222             this.startBox = this.el.getBox();
8223             this.startPoint = e.getXY();
8224             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8225                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8226
8227             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8228             this.overlay.show();
8229
8230             if(this.constrainTo) {
8231                 var ct = Roo.get(this.constrainTo);
8232                 this.resizeRegion = ct.getRegion().adjust(
8233                     ct.getFrameWidth('t'),
8234                     ct.getFrameWidth('l'),
8235                     -ct.getFrameWidth('b'),
8236                     -ct.getFrameWidth('r')
8237                 );
8238             }
8239
8240             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8241             this.proxy.show();
8242             this.proxy.setBox(this.startBox);
8243             if(!this.dynamic){
8244                 this.proxy.setStyle('visibility', 'visible');
8245             }
8246         }
8247     },
8248
8249     // private
8250     onMouseDown : function(handle, e){
8251         if(this.enabled){
8252             e.stopEvent();
8253             this.activeHandle = handle;
8254             this.startSizing(e, handle);
8255         }
8256     },
8257
8258     // private
8259     onMouseUp : function(e){
8260         var size = this.resizeElement();
8261         this.resizing = false;
8262         this.handleOut();
8263         this.overlay.hide();
8264         this.proxy.hide();
8265         this.fireEvent("resize", this, size.width, size.height, e);
8266     },
8267
8268     // private
8269     updateChildSize : function(){
8270         
8271         if(this.resizeChild){
8272             var el = this.el;
8273             var child = this.resizeChild;
8274             var adj = this.adjustments;
8275             if(el.dom.offsetWidth){
8276                 var b = el.getSize(true);
8277                 child.setSize(b.width+adj[0], b.height+adj[1]);
8278             }
8279             // Second call here for IE
8280             // The first call enables instant resizing and
8281             // the second call corrects scroll bars if they
8282             // exist
8283             if(Roo.isIE){
8284                 setTimeout(function(){
8285                     if(el.dom.offsetWidth){
8286                         var b = el.getSize(true);
8287                         child.setSize(b.width+adj[0], b.height+adj[1]);
8288                     }
8289                 }, 10);
8290             }
8291         }
8292     },
8293
8294     // private
8295     snap : function(value, inc, min){
8296         if(!inc || !value) {
8297             return value;
8298         }
8299         var newValue = value;
8300         var m = value % inc;
8301         if(m > 0){
8302             if(m > (inc/2)){
8303                 newValue = value + (inc-m);
8304             }else{
8305                 newValue = value - m;
8306             }
8307         }
8308         return Math.max(min, newValue);
8309     },
8310
8311     // private
8312     resizeElement : function(){
8313         var box = this.proxy.getBox();
8314         if(this.updateBox){
8315             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8316         }else{
8317             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8318         }
8319         this.updateChildSize();
8320         if(!this.dynamic){
8321             this.proxy.hide();
8322         }
8323         return box;
8324     },
8325
8326     // private
8327     constrain : function(v, diff, m, mx){
8328         if(v - diff < m){
8329             diff = v - m;
8330         }else if(v - diff > mx){
8331             diff = mx - v;
8332         }
8333         return diff;
8334     },
8335
8336     // private
8337     onMouseMove : function(e){
8338         
8339         if(this.enabled){
8340             try{// try catch so if something goes wrong the user doesn't get hung
8341
8342             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8343                 return;
8344             }
8345
8346             //var curXY = this.startPoint;
8347             var curSize = this.curSize || this.startBox;
8348             var x = this.startBox.x, y = this.startBox.y;
8349             var ox = x, oy = y;
8350             var w = curSize.width, h = curSize.height;
8351             var ow = w, oh = h;
8352             var mw = this.minWidth, mh = this.minHeight;
8353             var mxw = this.maxWidth, mxh = this.maxHeight;
8354             var wi = this.widthIncrement;
8355             var hi = this.heightIncrement;
8356
8357             var eventXY = e.getXY();
8358             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8359             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8360
8361             var pos = this.activeHandle.position;
8362
8363             switch(pos){
8364                 case "east":
8365                     w += diffX;
8366                     w = Math.min(Math.max(mw, w), mxw);
8367                     break;
8368              
8369                 case "south":
8370                     h += diffY;
8371                     h = Math.min(Math.max(mh, h), mxh);
8372                     break;
8373                 case "southeast":
8374                     w += diffX;
8375                     h += diffY;
8376                     w = Math.min(Math.max(mw, w), mxw);
8377                     h = Math.min(Math.max(mh, h), mxh);
8378                     break;
8379                 case "north":
8380                     diffY = this.constrain(h, diffY, mh, mxh);
8381                     y += diffY;
8382                     h -= diffY;
8383                     break;
8384                 case "hdrag":
8385                     
8386                     if (wi) {
8387                         var adiffX = Math.abs(diffX);
8388                         var sub = (adiffX % wi); // how much 
8389                         if (sub > (wi/2)) { // far enough to snap
8390                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8391                         } else {
8392                             // remove difference.. 
8393                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8394                         }
8395                     }
8396                     x += diffX;
8397                     x = Math.max(this.minX, x);
8398                     break;
8399                 case "west":
8400                     diffX = this.constrain(w, diffX, mw, mxw);
8401                     x += diffX;
8402                     w -= diffX;
8403                     break;
8404                 case "northeast":
8405                     w += diffX;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     diffY = this.constrain(h, diffY, mh, mxh);
8408                     y += diffY;
8409                     h -= diffY;
8410                     break;
8411                 case "northwest":
8412                     diffX = this.constrain(w, diffX, mw, mxw);
8413                     diffY = this.constrain(h, diffY, mh, mxh);
8414                     y += diffY;
8415                     h -= diffY;
8416                     x += diffX;
8417                     w -= diffX;
8418                     break;
8419                case "southwest":
8420                     diffX = this.constrain(w, diffX, mw, mxw);
8421                     h += diffY;
8422                     h = Math.min(Math.max(mh, h), mxh);
8423                     x += diffX;
8424                     w -= diffX;
8425                     break;
8426             }
8427
8428             var sw = this.snap(w, wi, mw);
8429             var sh = this.snap(h, hi, mh);
8430             if(sw != w || sh != h){
8431                 switch(pos){
8432                     case "northeast":
8433                         y -= sh - h;
8434                     break;
8435                     case "north":
8436                         y -= sh - h;
8437                         break;
8438                     case "southwest":
8439                         x -= sw - w;
8440                     break;
8441                     case "west":
8442                         x -= sw - w;
8443                         break;
8444                     case "northwest":
8445                         x -= sw - w;
8446                         y -= sh - h;
8447                     break;
8448                 }
8449                 w = sw;
8450                 h = sh;
8451             }
8452
8453             if(this.preserveRatio){
8454                 switch(pos){
8455                     case "southeast":
8456                     case "east":
8457                         h = oh * (w/ow);
8458                         h = Math.min(Math.max(mh, h), mxh);
8459                         w = ow * (h/oh);
8460                        break;
8461                     case "south":
8462                         w = ow * (h/oh);
8463                         w = Math.min(Math.max(mw, w), mxw);
8464                         h = oh * (w/ow);
8465                         break;
8466                     case "northeast":
8467                         w = ow * (h/oh);
8468                         w = Math.min(Math.max(mw, w), mxw);
8469                         h = oh * (w/ow);
8470                     break;
8471                     case "north":
8472                         var tw = w;
8473                         w = ow * (h/oh);
8474                         w = Math.min(Math.max(mw, w), mxw);
8475                         h = oh * (w/ow);
8476                         x += (tw - w) / 2;
8477                         break;
8478                     case "southwest":
8479                         h = oh * (w/ow);
8480                         h = Math.min(Math.max(mh, h), mxh);
8481                         var tw = w;
8482                         w = ow * (h/oh);
8483                         x += tw - w;
8484                         break;
8485                     case "west":
8486                         var th = h;
8487                         h = oh * (w/ow);
8488                         h = Math.min(Math.max(mh, h), mxh);
8489                         y += (th - h) / 2;
8490                         var tw = w;
8491                         w = ow * (h/oh);
8492                         x += tw - w;
8493                        break;
8494                     case "northwest":
8495                         var tw = w;
8496                         var th = h;
8497                         h = oh * (w/ow);
8498                         h = Math.min(Math.max(mh, h), mxh);
8499                         w = ow * (h/oh);
8500                         y += th - h;
8501                         x += tw - w;
8502                        break;
8503
8504                 }
8505             }
8506             if (pos == 'hdrag') {
8507                 w = ow;
8508             }
8509             this.proxy.setBounds(x, y, w, h);
8510             if(this.dynamic){
8511                 this.resizeElement();
8512             }
8513             }catch(e){}
8514         }
8515         this.fireEvent("resizing", this, x, y, w, h, e);
8516     },
8517
8518     // private
8519     handleOver : function(){
8520         if(this.enabled){
8521             this.el.addClass("x-resizable-over");
8522         }
8523     },
8524
8525     // private
8526     handleOut : function(){
8527         if(!this.resizing){
8528             this.el.removeClass("x-resizable-over");
8529         }
8530     },
8531
8532     /**
8533      * Returns the element this component is bound to.
8534      * @return {Roo.Element}
8535      */
8536     getEl : function(){
8537         return this.el;
8538     },
8539
8540     /**
8541      * Returns the resizeChild element (or null).
8542      * @return {Roo.Element}
8543      */
8544     getResizeChild : function(){
8545         return this.resizeChild;
8546     },
8547     groupHandler : function()
8548     {
8549         
8550     },
8551     /**
8552      * Destroys this resizable. If the element was wrapped and
8553      * removeEl is not true then the element remains.
8554      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8555      */
8556     destroy : function(removeEl){
8557         this.proxy.remove();
8558         if(this.overlay){
8559             this.overlay.removeAllListeners();
8560             this.overlay.remove();
8561         }
8562         var ps = Roo.Resizable.positions;
8563         for(var k in ps){
8564             if(typeof ps[k] != "function" && this[ps[k]]){
8565                 var h = this[ps[k]];
8566                 h.el.removeAllListeners();
8567                 h.el.remove();
8568             }
8569         }
8570         if(removeEl){
8571             this.el.update("");
8572             this.el.remove();
8573         }
8574     }
8575 });
8576
8577 // private
8578 // hash to map config positions to true positions
8579 Roo.Resizable.positions = {
8580     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8581     hd: "hdrag"
8582 };
8583
8584 // private
8585 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8586     if(!this.tpl){
8587         // only initialize the template if resizable is used
8588         var tpl = Roo.DomHelper.createTemplate(
8589             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8590         );
8591         tpl.compile();
8592         Roo.Resizable.Handle.prototype.tpl = tpl;
8593     }
8594     this.position = pos;
8595     this.rz = rz;
8596     // show north drag fro topdra
8597     var handlepos = pos == 'hdrag' ? 'north' : pos;
8598     
8599     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8600     if (pos == 'hdrag') {
8601         this.el.setStyle('cursor', 'pointer');
8602     }
8603     this.el.unselectable();
8604     if(transparent){
8605         this.el.setOpacity(0);
8606     }
8607     this.el.on("mousedown", this.onMouseDown, this);
8608     if(!disableTrackOver){
8609         this.el.on("mouseover", this.onMouseOver, this);
8610         this.el.on("mouseout", this.onMouseOut, this);
8611     }
8612 };
8613
8614 // private
8615 Roo.Resizable.Handle.prototype = {
8616     afterResize : function(rz){
8617         Roo.log('after?');
8618         // do nothing
8619     },
8620     // private
8621     onMouseDown : function(e){
8622         this.rz.onMouseDown(this, e);
8623     },
8624     // private
8625     onMouseOver : function(e){
8626         this.rz.handleOver(this, e);
8627     },
8628     // private
8629     onMouseOut : function(e){
8630         this.rz.handleOut(this, e);
8631     }
8632 };/*
8633  * Based on:
8634  * Ext JS Library 1.1.1
8635  * Copyright(c) 2006-2007, Ext JS, LLC.
8636  *
8637  * Originally Released Under LGPL - original licence link has changed is not relivant.
8638  *
8639  * Fork - LGPL
8640  * <script type="text/javascript">
8641  */
8642
8643 /**
8644  * @class Roo.Editor
8645  * @extends Roo.Component
8646  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8647  * @constructor
8648  * Create a new Editor
8649  * @param {Roo.form.Field} field The Field object (or descendant)
8650  * @param {Object} config The config object
8651  */
8652 Roo.Editor = function(field, config){
8653     Roo.Editor.superclass.constructor.call(this, config);
8654     this.field = field;
8655     this.addEvents({
8656         /**
8657              * @event beforestartedit
8658              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8659              * false from the handler of this event.
8660              * @param {Editor} this
8661              * @param {Roo.Element} boundEl The underlying element bound to this editor
8662              * @param {Mixed} value The field value being set
8663              */
8664         "beforestartedit" : true,
8665         /**
8666              * @event startedit
8667              * Fires when this editor is displayed
8668              * @param {Roo.Element} boundEl The underlying element bound to this editor
8669              * @param {Mixed} value The starting field value
8670              */
8671         "startedit" : true,
8672         /**
8673              * @event beforecomplete
8674              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8675              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8676              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8677              * event will not fire since no edit actually occurred.
8678              * @param {Editor} this
8679              * @param {Mixed} value The current field value
8680              * @param {Mixed} startValue The original field value
8681              */
8682         "beforecomplete" : true,
8683         /**
8684              * @event complete
8685              * Fires after editing is complete and any changed value has been written to the underlying field.
8686              * @param {Editor} this
8687              * @param {Mixed} value The current field value
8688              * @param {Mixed} startValue The original field value
8689              */
8690         "complete" : true,
8691         /**
8692          * @event specialkey
8693          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8694          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8695          * @param {Roo.form.Field} this
8696          * @param {Roo.EventObject} e The event object
8697          */
8698         "specialkey" : true
8699     });
8700 };
8701
8702 Roo.extend(Roo.Editor, Roo.Component, {
8703     /**
8704      * @cfg {Boolean/String} autosize
8705      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8706      * or "height" to adopt the height only (defaults to false)
8707      */
8708     /**
8709      * @cfg {Boolean} revertInvalid
8710      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8711      * validation fails (defaults to true)
8712      */
8713     /**
8714      * @cfg {Boolean} ignoreNoChange
8715      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8716      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8717      * will never be ignored.
8718      */
8719     /**
8720      * @cfg {Boolean} hideEl
8721      * False to keep the bound element visible while the editor is displayed (defaults to true)
8722      */
8723     /**
8724      * @cfg {Mixed} value
8725      * The data value of the underlying field (defaults to "")
8726      */
8727     value : "",
8728     /**
8729      * @cfg {String} alignment
8730      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8731      */
8732     alignment: "c-c?",
8733     /**
8734      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8735      * for bottom-right shadow (defaults to "frame")
8736      */
8737     shadow : "frame",
8738     /**
8739      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8740      */
8741     constrain : false,
8742     /**
8743      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8744      */
8745     completeOnEnter : false,
8746     /**
8747      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8748      */
8749     cancelOnEsc : false,
8750     /**
8751      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8752      */
8753     updateEl : false,
8754
8755     // private
8756     onRender : function(ct, position){
8757         this.el = new Roo.Layer({
8758             shadow: this.shadow,
8759             cls: "x-editor",
8760             parentEl : ct,
8761             shim : this.shim,
8762             shadowOffset:4,
8763             id: this.id,
8764             constrain: this.constrain
8765         });
8766         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8767         if(this.field.msgTarget != 'title'){
8768             this.field.msgTarget = 'qtip';
8769         }
8770         this.field.render(this.el);
8771         if(Roo.isGecko){
8772             this.field.el.dom.setAttribute('autocomplete', 'off');
8773         }
8774         this.field.on("specialkey", this.onSpecialKey, this);
8775         if(this.swallowKeys){
8776             this.field.el.swallowEvent(['keydown','keypress']);
8777         }
8778         this.field.show();
8779         this.field.on("blur", this.onBlur, this);
8780         if(this.field.grow){
8781             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8782         }
8783     },
8784
8785     onSpecialKey : function(field, e)
8786     {
8787         //Roo.log('editor onSpecialKey');
8788         if(this.completeOnEnter && e.getKey() == e.ENTER){
8789             e.stopEvent();
8790             this.completeEdit();
8791             return;
8792         }
8793         // do not fire special key otherwise it might hide close the editor...
8794         if(e.getKey() == e.ENTER){    
8795             return;
8796         }
8797         if(this.cancelOnEsc && e.getKey() == e.ESC){
8798             this.cancelEdit();
8799             return;
8800         } 
8801         this.fireEvent('specialkey', field, e);
8802     
8803     },
8804
8805     /**
8806      * Starts the editing process and shows the editor.
8807      * @param {String/HTMLElement/Element} el The element to edit
8808      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8809       * to the innerHTML of el.
8810      */
8811     startEdit : function(el, value){
8812         if(this.editing){
8813             this.completeEdit();
8814         }
8815         this.boundEl = Roo.get(el);
8816         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8817         if(!this.rendered){
8818             this.render(this.parentEl || document.body);
8819         }
8820         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8821             return;
8822         }
8823         this.startValue = v;
8824         this.field.setValue(v);
8825         if(this.autoSize){
8826             var sz = this.boundEl.getSize();
8827             switch(this.autoSize){
8828                 case "width":
8829                 this.setSize(sz.width,  "");
8830                 break;
8831                 case "height":
8832                 this.setSize("",  sz.height);
8833                 break;
8834                 default:
8835                 this.setSize(sz.width,  sz.height);
8836             }
8837         }
8838         this.el.alignTo(this.boundEl, this.alignment);
8839         this.editing = true;
8840         if(Roo.QuickTips){
8841             Roo.QuickTips.disable();
8842         }
8843         this.show();
8844     },
8845
8846     /**
8847      * Sets the height and width of this editor.
8848      * @param {Number} width The new width
8849      * @param {Number} height The new height
8850      */
8851     setSize : function(w, h){
8852         this.field.setSize(w, h);
8853         if(this.el){
8854             this.el.sync();
8855         }
8856     },
8857
8858     /**
8859      * Realigns the editor to the bound field based on the current alignment config value.
8860      */
8861     realign : function(){
8862         this.el.alignTo(this.boundEl, this.alignment);
8863     },
8864
8865     /**
8866      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8867      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8868      */
8869     completeEdit : function(remainVisible){
8870         if(!this.editing){
8871             return;
8872         }
8873         var v = this.getValue();
8874         if(this.revertInvalid !== false && !this.field.isValid()){
8875             v = this.startValue;
8876             this.cancelEdit(true);
8877         }
8878         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8879             this.editing = false;
8880             this.hide();
8881             return;
8882         }
8883         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8884             this.editing = false;
8885             if(this.updateEl && this.boundEl){
8886                 this.boundEl.update(v);
8887             }
8888             if(remainVisible !== true){
8889                 this.hide();
8890             }
8891             this.fireEvent("complete", this, v, this.startValue);
8892         }
8893     },
8894
8895     // private
8896     onShow : function(){
8897         this.el.show();
8898         if(this.hideEl !== false){
8899             this.boundEl.hide();
8900         }
8901         this.field.show();
8902         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8903             this.fixIEFocus = true;
8904             this.deferredFocus.defer(50, this);
8905         }else{
8906             this.field.focus();
8907         }
8908         this.fireEvent("startedit", this.boundEl, this.startValue);
8909     },
8910
8911     deferredFocus : function(){
8912         if(this.editing){
8913             this.field.focus();
8914         }
8915     },
8916
8917     /**
8918      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8919      * reverted to the original starting value.
8920      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8921      * cancel (defaults to false)
8922      */
8923     cancelEdit : function(remainVisible){
8924         if(this.editing){
8925             this.setValue(this.startValue);
8926             if(remainVisible !== true){
8927                 this.hide();
8928             }
8929         }
8930     },
8931
8932     // private
8933     onBlur : function(){
8934         if(this.allowBlur !== true && this.editing){
8935             this.completeEdit();
8936         }
8937     },
8938
8939     // private
8940     onHide : function(){
8941         if(this.editing){
8942             this.completeEdit();
8943             return;
8944         }
8945         this.field.blur();
8946         if(this.field.collapse){
8947             this.field.collapse();
8948         }
8949         this.el.hide();
8950         if(this.hideEl !== false){
8951             this.boundEl.show();
8952         }
8953         if(Roo.QuickTips){
8954             Roo.QuickTips.enable();
8955         }
8956     },
8957
8958     /**
8959      * Sets the data value of the editor
8960      * @param {Mixed} value Any valid value supported by the underlying field
8961      */
8962     setValue : function(v){
8963         this.field.setValue(v);
8964     },
8965
8966     /**
8967      * Gets the data value of the editor
8968      * @return {Mixed} The data value
8969      */
8970     getValue : function(){
8971         return this.field.getValue();
8972     }
8973 });/*
8974  * Based on:
8975  * Ext JS Library 1.1.1
8976  * Copyright(c) 2006-2007, Ext JS, LLC.
8977  *
8978  * Originally Released Under LGPL - original licence link has changed is not relivant.
8979  *
8980  * Fork - LGPL
8981  * <script type="text/javascript">
8982  */
8983  
8984 /**
8985  * @class Roo.BasicDialog
8986  * @extends Roo.util.Observable
8987  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8988  * <pre><code>
8989 var dlg = new Roo.BasicDialog("my-dlg", {
8990     height: 200,
8991     width: 300,
8992     minHeight: 100,
8993     minWidth: 150,
8994     modal: true,
8995     proxyDrag: true,
8996     shadow: true
8997 });
8998 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8999 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9000 dlg.addButton('Cancel', dlg.hide, dlg);
9001 dlg.show();
9002 </code></pre>
9003   <b>A Dialog should always be a direct child of the body element.</b>
9004  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9005  * @cfg {String} title Default text to display in the title bar (defaults to null)
9006  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9007  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9008  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9009  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9010  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9011  * (defaults to null with no animation)
9012  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9013  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9014  * property for valid values (defaults to 'all')
9015  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9016  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9017  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9018  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9019  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9020  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9021  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9022  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9023  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9024  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9025  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9026  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9027  * draggable = true (defaults to false)
9028  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9029  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9030  * shadow (defaults to false)
9031  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9032  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9033  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9034  * @cfg {Array} buttons Array of buttons
9035  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9036  * @constructor
9037  * Create a new BasicDialog.
9038  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9039  * @param {Object} config Configuration options
9040  */
9041 Roo.BasicDialog = function(el, config){
9042     this.el = Roo.get(el);
9043     var dh = Roo.DomHelper;
9044     if(!this.el && config && config.autoCreate){
9045         if(typeof config.autoCreate == "object"){
9046             if(!config.autoCreate.id){
9047                 config.autoCreate.id = el;
9048             }
9049             this.el = dh.append(document.body,
9050                         config.autoCreate, true);
9051         }else{
9052             this.el = dh.append(document.body,
9053                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9054         }
9055     }
9056     el = this.el;
9057     el.setDisplayed(true);
9058     el.hide = this.hideAction;
9059     this.id = el.id;
9060     el.addClass("x-dlg");
9061
9062     Roo.apply(this, config);
9063
9064     this.proxy = el.createProxy("x-dlg-proxy");
9065     this.proxy.hide = this.hideAction;
9066     this.proxy.setOpacity(.5);
9067     this.proxy.hide();
9068
9069     if(config.width){
9070         el.setWidth(config.width);
9071     }
9072     if(config.height){
9073         el.setHeight(config.height);
9074     }
9075     this.size = el.getSize();
9076     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9077         this.xy = [config.x,config.y];
9078     }else{
9079         this.xy = el.getCenterXY(true);
9080     }
9081     /** The header element @type Roo.Element */
9082     this.header = el.child("> .x-dlg-hd");
9083     /** The body element @type Roo.Element */
9084     this.body = el.child("> .x-dlg-bd");
9085     /** The footer element @type Roo.Element */
9086     this.footer = el.child("> .x-dlg-ft");
9087
9088     if(!this.header){
9089         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9090     }
9091     if(!this.body){
9092         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9093     }
9094
9095     this.header.unselectable();
9096     if(this.title){
9097         this.header.update(this.title);
9098     }
9099     // this element allows the dialog to be focused for keyboard event
9100     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9101     this.focusEl.swallowEvent("click", true);
9102
9103     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9104
9105     // wrap the body and footer for special rendering
9106     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9107     if(this.footer){
9108         this.bwrap.dom.appendChild(this.footer.dom);
9109     }
9110
9111     this.bg = this.el.createChild({
9112         tag: "div", cls:"x-dlg-bg",
9113         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9114     });
9115     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9116
9117
9118     if(this.autoScroll !== false && !this.autoTabs){
9119         this.body.setStyle("overflow", "auto");
9120     }
9121
9122     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9123
9124     if(this.closable !== false){
9125         this.el.addClass("x-dlg-closable");
9126         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9127         this.close.on("click", this.closeClick, this);
9128         this.close.addClassOnOver("x-dlg-close-over");
9129     }
9130     if(this.collapsible !== false){
9131         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9132         this.collapseBtn.on("click", this.collapseClick, this);
9133         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9134         this.header.on("dblclick", this.collapseClick, this);
9135     }
9136     if(this.resizable !== false){
9137         this.el.addClass("x-dlg-resizable");
9138         this.resizer = new Roo.Resizable(el, {
9139             minWidth: this.minWidth || 80,
9140             minHeight:this.minHeight || 80,
9141             handles: this.resizeHandles || "all",
9142             pinned: true
9143         });
9144         this.resizer.on("beforeresize", this.beforeResize, this);
9145         this.resizer.on("resize", this.onResize, this);
9146     }
9147     if(this.draggable !== false){
9148         el.addClass("x-dlg-draggable");
9149         if (!this.proxyDrag) {
9150             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9151         }
9152         else {
9153             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9154         }
9155         dd.setHandleElId(this.header.id);
9156         dd.endDrag = this.endMove.createDelegate(this);
9157         dd.startDrag = this.startMove.createDelegate(this);
9158         dd.onDrag = this.onDrag.createDelegate(this);
9159         dd.scroll = false;
9160         this.dd = dd;
9161     }
9162     if(this.modal){
9163         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9164         this.mask.enableDisplayMode("block");
9165         this.mask.hide();
9166         this.el.addClass("x-dlg-modal");
9167     }
9168     if(this.shadow){
9169         this.shadow = new Roo.Shadow({
9170             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9171             offset : this.shadowOffset
9172         });
9173     }else{
9174         this.shadowOffset = 0;
9175     }
9176     if(Roo.useShims && this.shim !== false){
9177         this.shim = this.el.createShim();
9178         this.shim.hide = this.hideAction;
9179         this.shim.hide();
9180     }else{
9181         this.shim = false;
9182     }
9183     if(this.autoTabs){
9184         this.initTabs();
9185     }
9186     if (this.buttons) { 
9187         var bts= this.buttons;
9188         this.buttons = [];
9189         Roo.each(bts, function(b) {
9190             this.addButton(b);
9191         }, this);
9192     }
9193     
9194     
9195     this.addEvents({
9196         /**
9197          * @event keydown
9198          * Fires when a key is pressed
9199          * @param {Roo.BasicDialog} this
9200          * @param {Roo.EventObject} e
9201          */
9202         "keydown" : true,
9203         /**
9204          * @event move
9205          * Fires when this dialog is moved by the user.
9206          * @param {Roo.BasicDialog} this
9207          * @param {Number} x The new page X
9208          * @param {Number} y The new page Y
9209          */
9210         "move" : true,
9211         /**
9212          * @event resize
9213          * Fires when this dialog is resized by the user.
9214          * @param {Roo.BasicDialog} this
9215          * @param {Number} width The new width
9216          * @param {Number} height The new height
9217          */
9218         "resize" : true,
9219         /**
9220          * @event beforehide
9221          * Fires before this dialog is hidden.
9222          * @param {Roo.BasicDialog} this
9223          */
9224         "beforehide" : true,
9225         /**
9226          * @event hide
9227          * Fires when this dialog is hidden.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "hide" : true,
9231         /**
9232          * @event beforeshow
9233          * Fires before this dialog is shown.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "beforeshow" : true,
9237         /**
9238          * @event show
9239          * Fires when this dialog is shown.
9240          * @param {Roo.BasicDialog} this
9241          */
9242         "show" : true
9243     });
9244     el.on("keydown", this.onKeyDown, this);
9245     el.on("mousedown", this.toFront, this);
9246     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9247     this.el.hide();
9248     Roo.DialogManager.register(this);
9249     Roo.BasicDialog.superclass.constructor.call(this);
9250 };
9251
9252 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9253     shadowOffset: Roo.isIE ? 6 : 5,
9254     minHeight: 80,
9255     minWidth: 200,
9256     minButtonWidth: 75,
9257     defaultButton: null,
9258     buttonAlign: "right",
9259     tabTag: 'div',
9260     firstShow: true,
9261
9262     /**
9263      * Sets the dialog title text
9264      * @param {String} text The title text to display
9265      * @return {Roo.BasicDialog} this
9266      */
9267     setTitle : function(text){
9268         this.header.update(text);
9269         return this;
9270     },
9271
9272     // private
9273     closeClick : function(){
9274         this.hide();
9275     },
9276
9277     // private
9278     collapseClick : function(){
9279         this[this.collapsed ? "expand" : "collapse"]();
9280     },
9281
9282     /**
9283      * Collapses the dialog to its minimized state (only the title bar is visible).
9284      * Equivalent to the user clicking the collapse dialog button.
9285      */
9286     collapse : function(){
9287         if(!this.collapsed){
9288             this.collapsed = true;
9289             this.el.addClass("x-dlg-collapsed");
9290             this.restoreHeight = this.el.getHeight();
9291             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9292         }
9293     },
9294
9295     /**
9296      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9297      * clicking the expand dialog button.
9298      */
9299     expand : function(){
9300         if(this.collapsed){
9301             this.collapsed = false;
9302             this.el.removeClass("x-dlg-collapsed");
9303             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9304         }
9305     },
9306
9307     /**
9308      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9309      * @return {Roo.TabPanel} The tabs component
9310      */
9311     initTabs : function(){
9312         var tabs = this.getTabs();
9313         while(tabs.getTab(0)){
9314             tabs.removeTab(0);
9315         }
9316         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9317             var dom = el.dom;
9318             tabs.addTab(Roo.id(dom), dom.title);
9319             dom.title = "";
9320         });
9321         tabs.activate(0);
9322         return tabs;
9323     },
9324
9325     // private
9326     beforeResize : function(){
9327         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9328     },
9329
9330     // private
9331     onResize : function(){
9332         this.refreshSize();
9333         this.syncBodyHeight();
9334         this.adjustAssets();
9335         this.focus();
9336         this.fireEvent("resize", this, this.size.width, this.size.height);
9337     },
9338
9339     // private
9340     onKeyDown : function(e){
9341         if(this.isVisible()){
9342             this.fireEvent("keydown", this, e);
9343         }
9344     },
9345
9346     /**
9347      * Resizes the dialog.
9348      * @param {Number} width
9349      * @param {Number} height
9350      * @return {Roo.BasicDialog} this
9351      */
9352     resizeTo : function(width, height){
9353         this.el.setSize(width, height);
9354         this.size = {width: width, height: height};
9355         this.syncBodyHeight();
9356         if(this.fixedcenter){
9357             this.center();
9358         }
9359         if(this.isVisible()){
9360             this.constrainXY();
9361             this.adjustAssets();
9362         }
9363         this.fireEvent("resize", this, width, height);
9364         return this;
9365     },
9366
9367
9368     /**
9369      * Resizes the dialog to fit the specified content size.
9370      * @param {Number} width
9371      * @param {Number} height
9372      * @return {Roo.BasicDialog} this
9373      */
9374     setContentSize : function(w, h){
9375         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9376         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9377         //if(!this.el.isBorderBox()){
9378             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9379             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9380         //}
9381         if(this.tabs){
9382             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9383             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9384         }
9385         this.resizeTo(w, h);
9386         return this;
9387     },
9388
9389     /**
9390      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9391      * executed in response to a particular key being pressed while the dialog is active.
9392      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9393      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394      * @param {Function} fn The function to call
9395      * @param {Object} scope (optional) The scope of the function
9396      * @return {Roo.BasicDialog} this
9397      */
9398     addKeyListener : function(key, fn, scope){
9399         var keyCode, shift, ctrl, alt;
9400         if(typeof key == "object" && !(key instanceof Array)){
9401             keyCode = key["key"];
9402             shift = key["shift"];
9403             ctrl = key["ctrl"];
9404             alt = key["alt"];
9405         }else{
9406             keyCode = key;
9407         }
9408         var handler = function(dlg, e){
9409             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9410                 var k = e.getKey();
9411                 if(keyCode instanceof Array){
9412                     for(var i = 0, len = keyCode.length; i < len; i++){
9413                         if(keyCode[i] == k){
9414                           fn.call(scope || window, dlg, k, e);
9415                           return;
9416                         }
9417                     }
9418                 }else{
9419                     if(k == keyCode){
9420                         fn.call(scope || window, dlg, k, e);
9421                     }
9422                 }
9423             }
9424         };
9425         this.on("keydown", handler);
9426         return this;
9427     },
9428
9429     /**
9430      * Returns the TabPanel component (creates it if it doesn't exist).
9431      * Note: If you wish to simply check for the existence of tabs without creating them,
9432      * check for a null 'tabs' property.
9433      * @return {Roo.TabPanel} The tabs component
9434      */
9435     getTabs : function(){
9436         if(!this.tabs){
9437             this.el.addClass("x-dlg-auto-tabs");
9438             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9439             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9440         }
9441         return this.tabs;
9442     },
9443
9444     /**
9445      * Adds a button to the footer section of the dialog.
9446      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9447      * object or a valid Roo.DomHelper element config
9448      * @param {Function} handler The function called when the button is clicked
9449      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9450      * @return {Roo.Button} The new button
9451      */
9452     addButton : function(config, handler, scope){
9453         var dh = Roo.DomHelper;
9454         if(!this.footer){
9455             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9456         }
9457         if(!this.btnContainer){
9458             var tb = this.footer.createChild({
9459
9460                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9462             }, null, true);
9463             this.btnContainer = tb.firstChild.firstChild.firstChild;
9464         }
9465         var bconfig = {
9466             handler: handler,
9467             scope: scope,
9468             minWidth: this.minButtonWidth,
9469             hideParent:true
9470         };
9471         if(typeof config == "string"){
9472             bconfig.text = config;
9473         }else{
9474             if(config.tag){
9475                 bconfig.dhconfig = config;
9476             }else{
9477                 Roo.apply(bconfig, config);
9478             }
9479         }
9480         var fc = false;
9481         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9482             bconfig.position = Math.max(0, bconfig.position);
9483             fc = this.btnContainer.childNodes[bconfig.position];
9484         }
9485          
9486         var btn = new Roo.Button(
9487             fc ? 
9488                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9489                 : this.btnContainer.appendChild(document.createElement("td")),
9490             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9491             bconfig
9492         );
9493         this.syncBodyHeight();
9494         if(!this.buttons){
9495             /**
9496              * Array of all the buttons that have been added to this dialog via addButton
9497              * @type Array
9498              */
9499             this.buttons = [];
9500         }
9501         this.buttons.push(btn);
9502         return btn;
9503     },
9504
9505     /**
9506      * Sets the default button to be focused when the dialog is displayed.
9507      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9508      * @return {Roo.BasicDialog} this
9509      */
9510     setDefaultButton : function(btn){
9511         this.defaultButton = btn;
9512         return this;
9513     },
9514
9515     // private
9516     getHeaderFooterHeight : function(safe){
9517         var height = 0;
9518         if(this.header){
9519            height += this.header.getHeight();
9520         }
9521         if(this.footer){
9522            var fm = this.footer.getMargins();
9523             height += (this.footer.getHeight()+fm.top+fm.bottom);
9524         }
9525         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9526         height += this.centerBg.getPadding("tb");
9527         return height;
9528     },
9529
9530     // private
9531     syncBodyHeight : function()
9532     {
9533         var bd = this.body, // the text
9534             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9535             bw = this.bwrap;
9536         var height = this.size.height - this.getHeaderFooterHeight(false);
9537         bd.setHeight(height-bd.getMargins("tb"));
9538         var hh = this.header.getHeight();
9539         var h = this.size.height-hh;
9540         cb.setHeight(h);
9541         
9542         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9543         bw.setHeight(h-cb.getPadding("tb"));
9544         
9545         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9546         bd.setWidth(bw.getWidth(true));
9547         if(this.tabs){
9548             this.tabs.syncHeight();
9549             if(Roo.isIE){
9550                 this.tabs.el.repaint();
9551             }
9552         }
9553     },
9554
9555     /**
9556      * Restores the previous state of the dialog if Roo.state is configured.
9557      * @return {Roo.BasicDialog} this
9558      */
9559     restoreState : function(){
9560         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9561         if(box && box.width){
9562             this.xy = [box.x, box.y];
9563             this.resizeTo(box.width, box.height);
9564         }
9565         return this;
9566     },
9567
9568     // private
9569     beforeShow : function(){
9570         this.expand();
9571         if(this.fixedcenter){
9572             this.xy = this.el.getCenterXY(true);
9573         }
9574         if(this.modal){
9575             Roo.get(document.body).addClass("x-body-masked");
9576             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9577             this.mask.show();
9578         }
9579         this.constrainXY();
9580     },
9581
9582     // private
9583     animShow : function(){
9584         var b = Roo.get(this.animateTarget).getBox();
9585         this.proxy.setSize(b.width, b.height);
9586         this.proxy.setLocation(b.x, b.y);
9587         this.proxy.show();
9588         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9589                     true, .35, this.showEl.createDelegate(this));
9590     },
9591
9592     /**
9593      * Shows the dialog.
9594      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9595      * @return {Roo.BasicDialog} this
9596      */
9597     show : function(animateTarget){
9598         if (this.fireEvent("beforeshow", this) === false){
9599             return;
9600         }
9601         if(this.syncHeightBeforeShow){
9602             this.syncBodyHeight();
9603         }else if(this.firstShow){
9604             this.firstShow = false;
9605             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9606         }
9607         this.animateTarget = animateTarget || this.animateTarget;
9608         if(!this.el.isVisible()){
9609             this.beforeShow();
9610             if(this.animateTarget && Roo.get(this.animateTarget)){
9611                 this.animShow();
9612             }else{
9613                 this.showEl();
9614             }
9615         }
9616         return this;
9617     },
9618
9619     // private
9620     showEl : function(){
9621         this.proxy.hide();
9622         this.el.setXY(this.xy);
9623         this.el.show();
9624         this.adjustAssets(true);
9625         this.toFront();
9626         this.focus();
9627         // IE peekaboo bug - fix found by Dave Fenwick
9628         if(Roo.isIE){
9629             this.el.repaint();
9630         }
9631         this.fireEvent("show", this);
9632     },
9633
9634     /**
9635      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9636      * dialog itself will receive focus.
9637      */
9638     focus : function(){
9639         if(this.defaultButton){
9640             this.defaultButton.focus();
9641         }else{
9642             this.focusEl.focus();
9643         }
9644     },
9645
9646     // private
9647     constrainXY : function(){
9648         if(this.constraintoviewport !== false){
9649             if(!this.viewSize){
9650                 if(this.container){
9651                     var s = this.container.getSize();
9652                     this.viewSize = [s.width, s.height];
9653                 }else{
9654                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9655                 }
9656             }
9657             var s = Roo.get(this.container||document).getScroll();
9658
9659             var x = this.xy[0], y = this.xy[1];
9660             var w = this.size.width, h = this.size.height;
9661             var vw = this.viewSize[0], vh = this.viewSize[1];
9662             // only move it if it needs it
9663             var moved = false;
9664             // first validate right/bottom
9665             if(x + w > vw+s.left){
9666                 x = vw - w;
9667                 moved = true;
9668             }
9669             if(y + h > vh+s.top){
9670                 y = vh - h;
9671                 moved = true;
9672             }
9673             // then make sure top/left isn't negative
9674             if(x < s.left){
9675                 x = s.left;
9676                 moved = true;
9677             }
9678             if(y < s.top){
9679                 y = s.top;
9680                 moved = true;
9681             }
9682             if(moved){
9683                 // cache xy
9684                 this.xy = [x, y];
9685                 if(this.isVisible()){
9686                     this.el.setLocation(x, y);
9687                     this.adjustAssets();
9688                 }
9689             }
9690         }
9691     },
9692
9693     // private
9694     onDrag : function(){
9695         if(!this.proxyDrag){
9696             this.xy = this.el.getXY();
9697             this.adjustAssets();
9698         }
9699     },
9700
9701     // private
9702     adjustAssets : function(doShow){
9703         var x = this.xy[0], y = this.xy[1];
9704         var w = this.size.width, h = this.size.height;
9705         if(doShow === true){
9706             if(this.shadow){
9707                 this.shadow.show(this.el);
9708             }
9709             if(this.shim){
9710                 this.shim.show();
9711             }
9712         }
9713         if(this.shadow && this.shadow.isVisible()){
9714             this.shadow.show(this.el);
9715         }
9716         if(this.shim && this.shim.isVisible()){
9717             this.shim.setBounds(x, y, w, h);
9718         }
9719     },
9720
9721     // private
9722     adjustViewport : function(w, h){
9723         if(!w || !h){
9724             w = Roo.lib.Dom.getViewWidth();
9725             h = Roo.lib.Dom.getViewHeight();
9726         }
9727         // cache the size
9728         this.viewSize = [w, h];
9729         if(this.modal && this.mask.isVisible()){
9730             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9731             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9732         }
9733         if(this.isVisible()){
9734             this.constrainXY();
9735         }
9736     },
9737
9738     /**
9739      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9740      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9741      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9742      */
9743     destroy : function(removeEl){
9744         if(this.isVisible()){
9745             this.animateTarget = null;
9746             this.hide();
9747         }
9748         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9749         if(this.tabs){
9750             this.tabs.destroy(removeEl);
9751         }
9752         Roo.destroy(
9753              this.shim,
9754              this.proxy,
9755              this.resizer,
9756              this.close,
9757              this.mask
9758         );
9759         if(this.dd){
9760             this.dd.unreg();
9761         }
9762         if(this.buttons){
9763            for(var i = 0, len = this.buttons.length; i < len; i++){
9764                this.buttons[i].destroy();
9765            }
9766         }
9767         this.el.removeAllListeners();
9768         if(removeEl === true){
9769             this.el.update("");
9770             this.el.remove();
9771         }
9772         Roo.DialogManager.unregister(this);
9773     },
9774
9775     // private
9776     startMove : function(){
9777         if(this.proxyDrag){
9778             this.proxy.show();
9779         }
9780         if(this.constraintoviewport !== false){
9781             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9782         }
9783     },
9784
9785     // private
9786     endMove : function(){
9787         if(!this.proxyDrag){
9788             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9789         }else{
9790             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9791             this.proxy.hide();
9792         }
9793         this.refreshSize();
9794         this.adjustAssets();
9795         this.focus();
9796         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9797     },
9798
9799     /**
9800      * Brings this dialog to the front of any other visible dialogs
9801      * @return {Roo.BasicDialog} this
9802      */
9803     toFront : function(){
9804         Roo.DialogManager.bringToFront(this);
9805         return this;
9806     },
9807
9808     /**
9809      * Sends this dialog to the back (under) of any other visible dialogs
9810      * @return {Roo.BasicDialog} this
9811      */
9812     toBack : function(){
9813         Roo.DialogManager.sendToBack(this);
9814         return this;
9815     },
9816
9817     /**
9818      * Centers this dialog in the viewport
9819      * @return {Roo.BasicDialog} this
9820      */
9821     center : function(){
9822         var xy = this.el.getCenterXY(true);
9823         this.moveTo(xy[0], xy[1]);
9824         return this;
9825     },
9826
9827     /**
9828      * Moves the dialog's top-left corner to the specified point
9829      * @param {Number} x
9830      * @param {Number} y
9831      * @return {Roo.BasicDialog} this
9832      */
9833     moveTo : function(x, y){
9834         this.xy = [x,y];
9835         if(this.isVisible()){
9836             this.el.setXY(this.xy);
9837             this.adjustAssets();
9838         }
9839         return this;
9840     },
9841
9842     /**
9843      * Aligns the dialog to the specified element
9844      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9845      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9846      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9847      * @return {Roo.BasicDialog} this
9848      */
9849     alignTo : function(element, position, offsets){
9850         this.xy = this.el.getAlignToXY(element, position, offsets);
9851         if(this.isVisible()){
9852             this.el.setXY(this.xy);
9853             this.adjustAssets();
9854         }
9855         return this;
9856     },
9857
9858     /**
9859      * Anchors an element to another element and realigns it when the window is resized.
9860      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9861      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9862      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9863      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9864      * is a number, it is used as the buffer delay (defaults to 50ms).
9865      * @return {Roo.BasicDialog} this
9866      */
9867     anchorTo : function(el, alignment, offsets, monitorScroll){
9868         var action = function(){
9869             this.alignTo(el, alignment, offsets);
9870         };
9871         Roo.EventManager.onWindowResize(action, this);
9872         var tm = typeof monitorScroll;
9873         if(tm != 'undefined'){
9874             Roo.EventManager.on(window, 'scroll', action, this,
9875                 {buffer: tm == 'number' ? monitorScroll : 50});
9876         }
9877         action.call(this);
9878         return this;
9879     },
9880
9881     /**
9882      * Returns true if the dialog is visible
9883      * @return {Boolean}
9884      */
9885     isVisible : function(){
9886         return this.el.isVisible();
9887     },
9888
9889     // private
9890     animHide : function(callback){
9891         var b = Roo.get(this.animateTarget).getBox();
9892         this.proxy.show();
9893         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9894         this.el.hide();
9895         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9896                     this.hideEl.createDelegate(this, [callback]));
9897     },
9898
9899     /**
9900      * Hides the dialog.
9901      * @param {Function} callback (optional) Function to call when the dialog is hidden
9902      * @return {Roo.BasicDialog} this
9903      */
9904     hide : function(callback){
9905         if (this.fireEvent("beforehide", this) === false){
9906             return;
9907         }
9908         if(this.shadow){
9909             this.shadow.hide();
9910         }
9911         if(this.shim) {
9912           this.shim.hide();
9913         }
9914         // sometimes animateTarget seems to get set.. causing problems...
9915         // this just double checks..
9916         if(this.animateTarget && Roo.get(this.animateTarget)) {
9917            this.animHide(callback);
9918         }else{
9919             this.el.hide();
9920             this.hideEl(callback);
9921         }
9922         return this;
9923     },
9924
9925     // private
9926     hideEl : function(callback){
9927         this.proxy.hide();
9928         if(this.modal){
9929             this.mask.hide();
9930             Roo.get(document.body).removeClass("x-body-masked");
9931         }
9932         this.fireEvent("hide", this);
9933         if(typeof callback == "function"){
9934             callback();
9935         }
9936     },
9937
9938     // private
9939     hideAction : function(){
9940         this.setLeft("-10000px");
9941         this.setTop("-10000px");
9942         this.setStyle("visibility", "hidden");
9943     },
9944
9945     // private
9946     refreshSize : function(){
9947         this.size = this.el.getSize();
9948         this.xy = this.el.getXY();
9949         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9950     },
9951
9952     // private
9953     // z-index is managed by the DialogManager and may be overwritten at any time
9954     setZIndex : function(index){
9955         if(this.modal){
9956             this.mask.setStyle("z-index", index);
9957         }
9958         if(this.shim){
9959             this.shim.setStyle("z-index", ++index);
9960         }
9961         if(this.shadow){
9962             this.shadow.setZIndex(++index);
9963         }
9964         this.el.setStyle("z-index", ++index);
9965         if(this.proxy){
9966             this.proxy.setStyle("z-index", ++index);
9967         }
9968         if(this.resizer){
9969             this.resizer.proxy.setStyle("z-index", ++index);
9970         }
9971
9972         this.lastZIndex = index;
9973     },
9974
9975     /**
9976      * Returns the element for this dialog
9977      * @return {Roo.Element} The underlying dialog Element
9978      */
9979     getEl : function(){
9980         return this.el;
9981     }
9982 });
9983
9984 /**
9985  * @class Roo.DialogManager
9986  * Provides global access to BasicDialogs that have been created and
9987  * support for z-indexing (layering) multiple open dialogs.
9988  */
9989 Roo.DialogManager = function(){
9990     var list = {};
9991     var accessList = [];
9992     var front = null;
9993
9994     // private
9995     var sortDialogs = function(d1, d2){
9996         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9997     };
9998
9999     // private
10000     var orderDialogs = function(){
10001         accessList.sort(sortDialogs);
10002         var seed = Roo.DialogManager.zseed;
10003         for(var i = 0, len = accessList.length; i < len; i++){
10004             var dlg = accessList[i];
10005             if(dlg){
10006                 dlg.setZIndex(seed + (i*10));
10007             }
10008         }
10009     };
10010
10011     return {
10012         /**
10013          * The starting z-index for BasicDialogs (defaults to 9000)
10014          * @type Number The z-index value
10015          */
10016         zseed : 9000,
10017
10018         // private
10019         register : function(dlg){
10020             list[dlg.id] = dlg;
10021             accessList.push(dlg);
10022         },
10023
10024         // private
10025         unregister : function(dlg){
10026             delete list[dlg.id];
10027             var i=0;
10028             var len=0;
10029             if(!accessList.indexOf){
10030                 for(  i = 0, len = accessList.length; i < len; i++){
10031                     if(accessList[i] == dlg){
10032                         accessList.splice(i, 1);
10033                         return;
10034                     }
10035                 }
10036             }else{
10037                  i = accessList.indexOf(dlg);
10038                 if(i != -1){
10039                     accessList.splice(i, 1);
10040                 }
10041             }
10042         },
10043
10044         /**
10045          * Gets a registered dialog by id
10046          * @param {String/Object} id The id of the dialog or a dialog
10047          * @return {Roo.BasicDialog} this
10048          */
10049         get : function(id){
10050             return typeof id == "object" ? id : list[id];
10051         },
10052
10053         /**
10054          * Brings the specified dialog to the front
10055          * @param {String/Object} dlg The id of the dialog or a dialog
10056          * @return {Roo.BasicDialog} this
10057          */
10058         bringToFront : function(dlg){
10059             dlg = this.get(dlg);
10060             if(dlg != front){
10061                 front = dlg;
10062                 dlg._lastAccess = new Date().getTime();
10063                 orderDialogs();
10064             }
10065             return dlg;
10066         },
10067
10068         /**
10069          * Sends the specified dialog to the back
10070          * @param {String/Object} dlg The id of the dialog or a dialog
10071          * @return {Roo.BasicDialog} this
10072          */
10073         sendToBack : function(dlg){
10074             dlg = this.get(dlg);
10075             dlg._lastAccess = -(new Date().getTime());
10076             orderDialogs();
10077             return dlg;
10078         },
10079
10080         /**
10081          * Hides all dialogs
10082          */
10083         hideAll : function(){
10084             for(var id in list){
10085                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10086                     list[id].hide();
10087                 }
10088             }
10089         }
10090     };
10091 }();
10092
10093 /**
10094  * @class Roo.LayoutDialog
10095  * @extends Roo.BasicDialog
10096  * @children Roo.ContentPanel
10097  * Dialog which provides adjustments for working with a layout in a Dialog.
10098  * Add your necessary layout config options to the dialog's config.<br>
10099  * Example usage (including a nested layout):
10100  * <pre><code>
10101 if(!dialog){
10102     dialog = new Roo.LayoutDialog("download-dlg", {
10103         modal: true,
10104         width:600,
10105         height:450,
10106         shadow:true,
10107         minWidth:500,
10108         minHeight:350,
10109         autoTabs:true,
10110         proxyDrag:true,
10111         // layout config merges with the dialog config
10112         center:{
10113             tabPosition: "top",
10114             alwaysShowTabs: true
10115         }
10116     });
10117     dialog.addKeyListener(27, dialog.hide, dialog);
10118     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10119     dialog.addButton("Build It!", this.getDownload, this);
10120
10121     // we can even add nested layouts
10122     var innerLayout = new Roo.BorderLayout("dl-inner", {
10123         east: {
10124             initialSize: 200,
10125             autoScroll:true,
10126             split:true
10127         },
10128         center: {
10129             autoScroll:true
10130         }
10131     });
10132     innerLayout.beginUpdate();
10133     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10134     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10135     innerLayout.endUpdate(true);
10136
10137     var layout = dialog.getLayout();
10138     layout.beginUpdate();
10139     layout.add("center", new Roo.ContentPanel("standard-panel",
10140                         {title: "Download the Source", fitToFrame:true}));
10141     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10142                {title: "Build your own roo.js"}));
10143     layout.getRegion("center").showPanel(sp);
10144     layout.endUpdate();
10145 }
10146 </code></pre>
10147     * @constructor
10148     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10149     * @param {Object} config configuration options
10150   */
10151 Roo.LayoutDialog = function(el, cfg){
10152     
10153     var config=  cfg;
10154     if (typeof(cfg) == 'undefined') {
10155         config = Roo.apply({}, el);
10156         // not sure why we use documentElement here.. - it should always be body.
10157         // IE7 borks horribly if we use documentElement.
10158         // webkit also does not like documentElement - it creates a body element...
10159         el = Roo.get( document.body || document.documentElement ).createChild();
10160         //config.autoCreate = true;
10161     }
10162     
10163     
10164     config.autoTabs = false;
10165     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10166     this.body.setStyle({overflow:"hidden", position:"relative"});
10167     this.layout = new Roo.BorderLayout(this.body.dom, config);
10168     this.layout.monitorWindowResize = false;
10169     this.el.addClass("x-dlg-auto-layout");
10170     // fix case when center region overwrites center function
10171     this.center = Roo.BasicDialog.prototype.center;
10172     this.on("show", this.layout.layout, this.layout, true);
10173     if (config.items) {
10174         var xitems = config.items;
10175         delete config.items;
10176         Roo.each(xitems, this.addxtype, this);
10177     }
10178     
10179     
10180 };
10181 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10182     
10183     
10184     /**
10185      * @cfg {Roo.LayoutRegion} east  
10186      */
10187     /**
10188      * @cfg {Roo.LayoutRegion} west
10189      */
10190     /**
10191      * @cfg {Roo.LayoutRegion} south
10192      */
10193     /**
10194      * @cfg {Roo.LayoutRegion} north
10195      */
10196     /**
10197      * @cfg {Roo.LayoutRegion} center
10198      */
10199     /**
10200      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10201      */
10202     
10203     
10204     /**
10205      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10206      * @deprecated
10207      */
10208     endUpdate : function(){
10209         this.layout.endUpdate();
10210     },
10211
10212     /**
10213      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10214      *  @deprecated
10215      */
10216     beginUpdate : function(){
10217         this.layout.beginUpdate();
10218     },
10219
10220     /**
10221      * Get the BorderLayout for this dialog
10222      * @return {Roo.BorderLayout}
10223      */
10224     getLayout : function(){
10225         return this.layout;
10226     },
10227
10228     showEl : function(){
10229         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10230         if(Roo.isIE7){
10231             this.layout.layout();
10232         }
10233     },
10234
10235     // private
10236     // Use the syncHeightBeforeShow config option to control this automatically
10237     syncBodyHeight : function(){
10238         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10239         if(this.layout){this.layout.layout();}
10240     },
10241     
10242       /**
10243      * Add an xtype element (actually adds to the layout.)
10244      * @return {Object} xdata xtype object data.
10245      */
10246     
10247     addxtype : function(c) {
10248         return this.layout.addxtype(c);
10249     }
10250 });/*
10251  * Based on:
10252  * Ext JS Library 1.1.1
10253  * Copyright(c) 2006-2007, Ext JS, LLC.
10254  *
10255  * Originally Released Under LGPL - original licence link has changed is not relivant.
10256  *
10257  * Fork - LGPL
10258  * <script type="text/javascript">
10259  */
10260  
10261 /**
10262  * @class Roo.MessageBox
10263  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10264  * Example usage:
10265  *<pre><code>
10266 // Basic alert:
10267 Roo.Msg.alert('Status', 'Changes saved successfully.');
10268
10269 // Prompt for user data:
10270 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10271     if (btn == 'ok'){
10272         // process text value...
10273     }
10274 });
10275
10276 // Show a dialog using config options:
10277 Roo.Msg.show({
10278    title:'Save Changes?',
10279    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10280    buttons: Roo.Msg.YESNOCANCEL,
10281    fn: processResult,
10282    animEl: 'elId'
10283 });
10284 </code></pre>
10285  * @singleton
10286  */
10287 Roo.MessageBox = function(){
10288     var dlg, opt, mask, waitTimer;
10289     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10290     var buttons, activeTextEl, bwidth;
10291
10292     // private
10293     var handleButton = function(button){
10294         dlg.hide();
10295         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10296     };
10297
10298     // private
10299     var handleHide = function(){
10300         if(opt && opt.cls){
10301             dlg.el.removeClass(opt.cls);
10302         }
10303         if(waitTimer){
10304             Roo.TaskMgr.stop(waitTimer);
10305             waitTimer = null;
10306         }
10307     };
10308
10309     // private
10310     var updateButtons = function(b){
10311         var width = 0;
10312         if(!b){
10313             buttons["ok"].hide();
10314             buttons["cancel"].hide();
10315             buttons["yes"].hide();
10316             buttons["no"].hide();
10317             dlg.footer.dom.style.display = 'none';
10318             return width;
10319         }
10320         dlg.footer.dom.style.display = '';
10321         for(var k in buttons){
10322             if(typeof buttons[k] != "function"){
10323                 if(b[k]){
10324                     buttons[k].show();
10325                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10326                     width += buttons[k].el.getWidth()+15;
10327                 }else{
10328                     buttons[k].hide();
10329                 }
10330             }
10331         }
10332         return width;
10333     };
10334
10335     // private
10336     var handleEsc = function(d, k, e){
10337         if(opt && opt.closable !== false){
10338             dlg.hide();
10339         }
10340         if(e){
10341             e.stopEvent();
10342         }
10343     };
10344
10345     return {
10346         /**
10347          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10348          * @return {Roo.BasicDialog} The BasicDialog element
10349          */
10350         getDialog : function(){
10351            if(!dlg){
10352                 dlg = new Roo.BasicDialog("x-msg-box", {
10353                     autoCreate : true,
10354                     shadow: true,
10355                     draggable: true,
10356                     resizable:false,
10357                     constraintoviewport:false,
10358                     fixedcenter:true,
10359                     collapsible : false,
10360                     shim:true,
10361                     modal: true,
10362                     width:400, height:100,
10363                     buttonAlign:"center",
10364                     closeClick : function(){
10365                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10366                             handleButton("no");
10367                         }else{
10368                             handleButton("cancel");
10369                         }
10370                     }
10371                 });
10372                 dlg.on("hide", handleHide);
10373                 mask = dlg.mask;
10374                 dlg.addKeyListener(27, handleEsc);
10375                 buttons = {};
10376                 var bt = this.buttonText;
10377                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10378                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10379                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10380                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10381                 bodyEl = dlg.body.createChild({
10382
10383                     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>'
10384                 });
10385                 msgEl = bodyEl.dom.firstChild;
10386                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10387                 textboxEl.enableDisplayMode();
10388                 textboxEl.addKeyListener([10,13], function(){
10389                     if(dlg.isVisible() && opt && opt.buttons){
10390                         if(opt.buttons.ok){
10391                             handleButton("ok");
10392                         }else if(opt.buttons.yes){
10393                             handleButton("yes");
10394                         }
10395                     }
10396                 });
10397                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10398                 textareaEl.enableDisplayMode();
10399                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10400                 progressEl.enableDisplayMode();
10401                 var pf = progressEl.dom.firstChild;
10402                 if (pf) {
10403                     pp = Roo.get(pf.firstChild);
10404                     pp.setHeight(pf.offsetHeight);
10405                 }
10406                 
10407             }
10408             return dlg;
10409         },
10410
10411         /**
10412          * Updates the message box body text
10413          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10414          * the XHTML-compliant non-breaking space character '&amp;#160;')
10415          * @return {Roo.MessageBox} This message box
10416          */
10417         updateText : function(text){
10418             if(!dlg.isVisible() && !opt.width){
10419                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10420             }
10421             msgEl.innerHTML = text || '&#160;';
10422       
10423             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10424             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10425             var w = Math.max(
10426                     Math.min(opt.width || cw , this.maxWidth), 
10427                     Math.max(opt.minWidth || this.minWidth, bwidth)
10428             );
10429             if(opt.prompt){
10430                 activeTextEl.setWidth(w);
10431             }
10432             if(dlg.isVisible()){
10433                 dlg.fixedcenter = false;
10434             }
10435             // to big, make it scroll. = But as usual stupid IE does not support
10436             // !important..
10437             
10438             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10439                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10440                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10441             } else {
10442                 bodyEl.dom.style.height = '';
10443                 bodyEl.dom.style.overflowY = '';
10444             }
10445             if (cw > w) {
10446                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10447             } else {
10448                 bodyEl.dom.style.overflowX = '';
10449             }
10450             
10451             dlg.setContentSize(w, bodyEl.getHeight());
10452             if(dlg.isVisible()){
10453                 dlg.fixedcenter = true;
10454             }
10455             return this;
10456         },
10457
10458         /**
10459          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10460          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10461          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10462          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10463          * @return {Roo.MessageBox} This message box
10464          */
10465         updateProgress : function(value, text){
10466             if(text){
10467                 this.updateText(text);
10468             }
10469             if (pp) { // weird bug on my firefox - for some reason this is not defined
10470                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10471             }
10472             return this;
10473         },        
10474
10475         /**
10476          * Returns true if the message box is currently displayed
10477          * @return {Boolean} True if the message box is visible, else false
10478          */
10479         isVisible : function(){
10480             return dlg && dlg.isVisible();  
10481         },
10482
10483         /**
10484          * Hides the message box if it is displayed
10485          */
10486         hide : function(){
10487             if(this.isVisible()){
10488                 dlg.hide();
10489             }  
10490         },
10491
10492         /**
10493          * Displays a new message box, or reinitializes an existing message box, based on the config options
10494          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10495          * The following config object properties are supported:
10496          * <pre>
10497 Property    Type             Description
10498 ----------  ---------------  ------------------------------------------------------------------------------------
10499 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10500                                    closes (defaults to undefined)
10501 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10502                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10503 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10504                                    progress and wait dialogs will ignore this property and always hide the
10505                                    close button as they can only be closed programmatically.
10506 cls               String           A custom CSS class to apply to the message box element
10507 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10508                                    displayed (defaults to 75)
10509 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10510                                    function will be btn (the name of the button that was clicked, if applicable,
10511                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10512                                    Progress and wait dialogs will ignore this option since they do not respond to
10513                                    user actions and can only be closed programmatically, so any required function
10514                                    should be called by the same code after it closes the dialog.
10515 icon              String           A CSS class that provides a background image to be used as an icon for
10516                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10517 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10518 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10519 modal             Boolean          False to allow user interaction with the page while the message box is
10520                                    displayed (defaults to true)
10521 msg               String           A string that will replace the existing message box body text (defaults
10522                                    to the XHTML-compliant non-breaking space character '&#160;')
10523 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10524 progress          Boolean          True to display a progress bar (defaults to false)
10525 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10526 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10527 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10528 title             String           The title text
10529 value             String           The string value to set into the active textbox element if displayed
10530 wait              Boolean          True to display a progress bar (defaults to false)
10531 width             Number           The width of the dialog in pixels
10532 </pre>
10533          *
10534          * Example usage:
10535          * <pre><code>
10536 Roo.Msg.show({
10537    title: 'Address',
10538    msg: 'Please enter your address:',
10539    width: 300,
10540    buttons: Roo.MessageBox.OKCANCEL,
10541    multiline: true,
10542    fn: saveAddress,
10543    animEl: 'addAddressBtn'
10544 });
10545 </code></pre>
10546          * @param {Object} config Configuration options
10547          * @return {Roo.MessageBox} This message box
10548          */
10549         show : function(options)
10550         {
10551             
10552             // this causes nightmares if you show one dialog after another
10553             // especially on callbacks..
10554              
10555             if(this.isVisible()){
10556                 
10557                 this.hide();
10558                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10559                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10560                 Roo.log("New Dialog Message:" +  options.msg )
10561                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10562                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10563                 
10564             }
10565             var d = this.getDialog();
10566             opt = options;
10567             d.setTitle(opt.title || "&#160;");
10568             d.close.setDisplayed(opt.closable !== false);
10569             activeTextEl = textboxEl;
10570             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10571             if(opt.prompt){
10572                 if(opt.multiline){
10573                     textboxEl.hide();
10574                     textareaEl.show();
10575                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10576                         opt.multiline : this.defaultTextHeight);
10577                     activeTextEl = textareaEl;
10578                 }else{
10579                     textboxEl.show();
10580                     textareaEl.hide();
10581                 }
10582             }else{
10583                 textboxEl.hide();
10584                 textareaEl.hide();
10585             }
10586             progressEl.setDisplayed(opt.progress === true);
10587             this.updateProgress(0);
10588             activeTextEl.dom.value = opt.value || "";
10589             if(opt.prompt){
10590                 dlg.setDefaultButton(activeTextEl);
10591             }else{
10592                 var bs = opt.buttons;
10593                 var db = null;
10594                 if(bs && bs.ok){
10595                     db = buttons["ok"];
10596                 }else if(bs && bs.yes){
10597                     db = buttons["yes"];
10598                 }
10599                 dlg.setDefaultButton(db);
10600             }
10601             bwidth = updateButtons(opt.buttons);
10602             this.updateText(opt.msg);
10603             if(opt.cls){
10604                 d.el.addClass(opt.cls);
10605             }
10606             d.proxyDrag = opt.proxyDrag === true;
10607             d.modal = opt.modal !== false;
10608             d.mask = opt.modal !== false ? mask : false;
10609             if(!d.isVisible()){
10610                 // force it to the end of the z-index stack so it gets a cursor in FF
10611                 document.body.appendChild(dlg.el.dom);
10612                 d.animateTarget = null;
10613                 d.show(options.animEl);
10614             }
10615             return this;
10616         },
10617
10618         /**
10619          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10620          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10621          * and closing the message box when the process is complete.
10622          * @param {String} title The title bar text
10623          * @param {String} msg The message box body text
10624          * @return {Roo.MessageBox} This message box
10625          */
10626         progress : function(title, msg){
10627             this.show({
10628                 title : title,
10629                 msg : msg,
10630                 buttons: false,
10631                 progress:true,
10632                 closable:false,
10633                 minWidth: this.minProgressWidth,
10634                 modal : true
10635             });
10636             return this;
10637         },
10638
10639         /**
10640          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10641          * If a callback function is passed it will be called after the user clicks the button, and the
10642          * id of the button that was clicked will be passed as the only parameter to the callback
10643          * (could also be the top-right close button).
10644          * @param {String} title The title bar text
10645          * @param {String} msg The message box body text
10646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10647          * @param {Object} scope (optional) The scope of the callback function
10648          * @return {Roo.MessageBox} This message box
10649          */
10650         alert : function(title, msg, fn, scope){
10651             this.show({
10652                 title : title,
10653                 msg : msg,
10654                 buttons: this.OK,
10655                 fn: fn,
10656                 scope : scope,
10657                 modal : true
10658             });
10659             return this;
10660         },
10661
10662         /**
10663          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10664          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10665          * You are responsible for closing the message box when the process is complete.
10666          * @param {String} msg The message box body text
10667          * @param {String} title (optional) The title bar text
10668          * @return {Roo.MessageBox} This message box
10669          */
10670         wait : function(msg, title){
10671             this.show({
10672                 title : title,
10673                 msg : msg,
10674                 buttons: false,
10675                 closable:false,
10676                 progress:true,
10677                 modal:true,
10678                 width:300,
10679                 wait:true
10680             });
10681             waitTimer = Roo.TaskMgr.start({
10682                 run: function(i){
10683                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10684                 },
10685                 interval: 1000
10686             });
10687             return this;
10688         },
10689
10690         /**
10691          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10692          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10693          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10694          * @param {String} title The title bar text
10695          * @param {String} msg The message box body text
10696          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10697          * @param {Object} scope (optional) The scope of the callback function
10698          * @return {Roo.MessageBox} This message box
10699          */
10700         confirm : function(title, msg, fn, scope){
10701             this.show({
10702                 title : title,
10703                 msg : msg,
10704                 buttons: this.YESNO,
10705                 fn: fn,
10706                 scope : scope,
10707                 modal : true
10708             });
10709             return this;
10710         },
10711
10712         /**
10713          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10714          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10715          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10716          * (could also be the top-right close button) and the text that was entered will be passed as the two
10717          * parameters to the callback.
10718          * @param {String} title The title bar text
10719          * @param {String} msg The message box body text
10720          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10721          * @param {Object} scope (optional) The scope of the callback function
10722          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10723          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10724          * @return {Roo.MessageBox} This message box
10725          */
10726         prompt : function(title, msg, fn, scope, multiline){
10727             this.show({
10728                 title : title,
10729                 msg : msg,
10730                 buttons: this.OKCANCEL,
10731                 fn: fn,
10732                 minWidth:250,
10733                 scope : scope,
10734                 prompt:true,
10735                 multiline: multiline,
10736                 modal : true
10737             });
10738             return this;
10739         },
10740
10741         /**
10742          * Button config that displays a single OK button
10743          * @type Object
10744          */
10745         OK : {ok:true},
10746         /**
10747          * Button config that displays Yes and No buttons
10748          * @type Object
10749          */
10750         YESNO : {yes:true, no:true},
10751         /**
10752          * Button config that displays OK and Cancel buttons
10753          * @type Object
10754          */
10755         OKCANCEL : {ok:true, cancel:true},
10756         /**
10757          * Button config that displays Yes, No and Cancel buttons
10758          * @type Object
10759          */
10760         YESNOCANCEL : {yes:true, no:true, cancel:true},
10761
10762         /**
10763          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10764          * @type Number
10765          */
10766         defaultTextHeight : 75,
10767         /**
10768          * The maximum width in pixels of the message box (defaults to 600)
10769          * @type Number
10770          */
10771         maxWidth : 600,
10772         /**
10773          * The minimum width in pixels of the message box (defaults to 100)
10774          * @type Number
10775          */
10776         minWidth : 100,
10777         /**
10778          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10779          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10780          * @type Number
10781          */
10782         minProgressWidth : 250,
10783         /**
10784          * An object containing the default button text strings that can be overriden for localized language support.
10785          * Supported properties are: ok, cancel, yes and no.
10786          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10787          * @type Object
10788          */
10789         buttonText : {
10790             ok : "OK",
10791             cancel : "Cancel",
10792             yes : "Yes",
10793             no : "No"
10794         }
10795     };
10796 }();
10797
10798 /**
10799  * Shorthand for {@link Roo.MessageBox}
10800  */
10801 Roo.Msg = Roo.MessageBox;/*
10802  * Based on:
10803  * Ext JS Library 1.1.1
10804  * Copyright(c) 2006-2007, Ext JS, LLC.
10805  *
10806  * Originally Released Under LGPL - original licence link has changed is not relivant.
10807  *
10808  * Fork - LGPL
10809  * <script type="text/javascript">
10810  */
10811 /**
10812  * @class Roo.QuickTips
10813  * Provides attractive and customizable tooltips for any element.
10814  * @singleton
10815  */
10816 Roo.QuickTips = function(){
10817     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10818     var ce, bd, xy, dd;
10819     var visible = false, disabled = true, inited = false;
10820     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10821     
10822     var onOver = function(e){
10823         if(disabled){
10824             return;
10825         }
10826         var t = e.getTarget();
10827         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10828             return;
10829         }
10830         if(ce && t == ce.el){
10831             clearTimeout(hideProc);
10832             return;
10833         }
10834         if(t && tagEls[t.id]){
10835             tagEls[t.id].el = t;
10836             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10837             return;
10838         }
10839         var ttp, et = Roo.fly(t);
10840         var ns = cfg.namespace;
10841         if(tm.interceptTitles && t.title){
10842             ttp = t.title;
10843             t.qtip = ttp;
10844             t.removeAttribute("title");
10845             e.preventDefault();
10846         }else{
10847             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10848         }
10849         if(ttp){
10850             showProc = show.defer(tm.showDelay, tm, [{
10851                 el: t, 
10852                 text: ttp.replace(/\\n/g,'<br/>'),
10853                 width: et.getAttributeNS(ns, cfg.width),
10854                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10855                 title: et.getAttributeNS(ns, cfg.title),
10856                     cls: et.getAttributeNS(ns, cfg.cls)
10857             }]);
10858         }
10859     };
10860     
10861     var onOut = function(e){
10862         clearTimeout(showProc);
10863         var t = e.getTarget();
10864         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10865             hideProc = setTimeout(hide, tm.hideDelay);
10866         }
10867     };
10868     
10869     var onMove = function(e){
10870         if(disabled){
10871             return;
10872         }
10873         xy = e.getXY();
10874         xy[1] += 18;
10875         if(tm.trackMouse && ce){
10876             el.setXY(xy);
10877         }
10878     };
10879     
10880     var onDown = function(e){
10881         clearTimeout(showProc);
10882         clearTimeout(hideProc);
10883         if(!e.within(el)){
10884             if(tm.hideOnClick){
10885                 hide();
10886                 tm.disable();
10887                 tm.enable.defer(100, tm);
10888             }
10889         }
10890     };
10891     
10892     var getPad = function(){
10893         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10894     };
10895
10896     var show = function(o){
10897         if(disabled){
10898             return;
10899         }
10900         clearTimeout(dismissProc);
10901         ce = o;
10902         if(removeCls){ // in case manually hidden
10903             el.removeClass(removeCls);
10904             removeCls = null;
10905         }
10906         if(ce.cls){
10907             el.addClass(ce.cls);
10908             removeCls = ce.cls;
10909         }
10910         if(ce.title){
10911             tipTitle.update(ce.title);
10912             tipTitle.show();
10913         }else{
10914             tipTitle.update('');
10915             tipTitle.hide();
10916         }
10917         el.dom.style.width  = tm.maxWidth+'px';
10918         //tipBody.dom.style.width = '';
10919         tipBodyText.update(o.text);
10920         var p = getPad(), w = ce.width;
10921         if(!w){
10922             var td = tipBodyText.dom;
10923             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10924             if(aw > tm.maxWidth){
10925                 w = tm.maxWidth;
10926             }else if(aw < tm.minWidth){
10927                 w = tm.minWidth;
10928             }else{
10929                 w = aw;
10930             }
10931         }
10932         //tipBody.setWidth(w);
10933         el.setWidth(parseInt(w, 10) + p);
10934         if(ce.autoHide === false){
10935             close.setDisplayed(true);
10936             if(dd){
10937                 dd.unlock();
10938             }
10939         }else{
10940             close.setDisplayed(false);
10941             if(dd){
10942                 dd.lock();
10943             }
10944         }
10945         if(xy){
10946             el.avoidY = xy[1]-18;
10947             el.setXY(xy);
10948         }
10949         if(tm.animate){
10950             el.setOpacity(.1);
10951             el.setStyle("visibility", "visible");
10952             el.fadeIn({callback: afterShow});
10953         }else{
10954             afterShow();
10955         }
10956     };
10957     
10958     var afterShow = function(){
10959         if(ce){
10960             el.show();
10961             esc.enable();
10962             if(tm.autoDismiss && ce.autoHide !== false){
10963                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10964             }
10965         }
10966     };
10967     
10968     var hide = function(noanim){
10969         clearTimeout(dismissProc);
10970         clearTimeout(hideProc);
10971         ce = null;
10972         if(el.isVisible()){
10973             esc.disable();
10974             if(noanim !== true && tm.animate){
10975                 el.fadeOut({callback: afterHide});
10976             }else{
10977                 afterHide();
10978             } 
10979         }
10980     };
10981     
10982     var afterHide = function(){
10983         el.hide();
10984         if(removeCls){
10985             el.removeClass(removeCls);
10986             removeCls = null;
10987         }
10988     };
10989     
10990     return {
10991         /**
10992         * @cfg {Number} minWidth
10993         * The minimum width of the quick tip (defaults to 40)
10994         */
10995        minWidth : 40,
10996         /**
10997         * @cfg {Number} maxWidth
10998         * The maximum width of the quick tip (defaults to 300)
10999         */
11000        maxWidth : 300,
11001         /**
11002         * @cfg {Boolean} interceptTitles
11003         * True to automatically use the element's DOM title value if available (defaults to false)
11004         */
11005        interceptTitles : false,
11006         /**
11007         * @cfg {Boolean} trackMouse
11008         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11009         */
11010        trackMouse : false,
11011         /**
11012         * @cfg {Boolean} hideOnClick
11013         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11014         */
11015        hideOnClick : true,
11016         /**
11017         * @cfg {Number} showDelay
11018         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11019         */
11020        showDelay : 500,
11021         /**
11022         * @cfg {Number} hideDelay
11023         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11024         */
11025        hideDelay : 200,
11026         /**
11027         * @cfg {Boolean} autoHide
11028         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11029         * Used in conjunction with hideDelay.
11030         */
11031        autoHide : true,
11032         /**
11033         * @cfg {Boolean}
11034         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11035         * (defaults to true).  Used in conjunction with autoDismissDelay.
11036         */
11037        autoDismiss : true,
11038         /**
11039         * @cfg {Number}
11040         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11041         */
11042        autoDismissDelay : 5000,
11043        /**
11044         * @cfg {Boolean} animate
11045         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11046         */
11047        animate : false,
11048
11049        /**
11050         * @cfg {String} title
11051         * Title text to display (defaults to '').  This can be any valid HTML markup.
11052         */
11053         title: '',
11054        /**
11055         * @cfg {String} text
11056         * Body text to display (defaults to '').  This can be any valid HTML markup.
11057         */
11058         text : '',
11059        /**
11060         * @cfg {String} cls
11061         * A CSS class to apply to the base quick tip element (defaults to '').
11062         */
11063         cls : '',
11064        /**
11065         * @cfg {Number} width
11066         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11067         * minWidth or maxWidth.
11068         */
11069         width : null,
11070
11071     /**
11072      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11073      * or display QuickTips in a page.
11074      */
11075        init : function(){
11076           tm = Roo.QuickTips;
11077           cfg = tm.tagConfig;
11078           if(!inited){
11079               if(!Roo.isReady){ // allow calling of init() before onReady
11080                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11081                   return;
11082               }
11083               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11084               el.fxDefaults = {stopFx: true};
11085               // maximum custom styling
11086               //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>');
11087               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>');              
11088               tipTitle = el.child('h3');
11089               tipTitle.enableDisplayMode("block");
11090               tipBody = el.child('div.x-tip-bd');
11091               tipBodyText = el.child('div.x-tip-bd-inner');
11092               //bdLeft = el.child('div.x-tip-bd-left');
11093               //bdRight = el.child('div.x-tip-bd-right');
11094               close = el.child('div.x-tip-close');
11095               close.enableDisplayMode("block");
11096               close.on("click", hide);
11097               var d = Roo.get(document);
11098               d.on("mousedown", onDown);
11099               d.on("mouseover", onOver);
11100               d.on("mouseout", onOut);
11101               d.on("mousemove", onMove);
11102               esc = d.addKeyListener(27, hide);
11103               esc.disable();
11104               if(Roo.dd.DD){
11105                   dd = el.initDD("default", null, {
11106                       onDrag : function(){
11107                           el.sync();  
11108                       }
11109                   });
11110                   dd.setHandleElId(tipTitle.id);
11111                   dd.lock();
11112               }
11113               inited = true;
11114           }
11115           this.enable(); 
11116        },
11117
11118     /**
11119      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11120      * are supported:
11121      * <pre>
11122 Property    Type                   Description
11123 ----------  ---------------------  ------------------------------------------------------------------------
11124 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11125      * </ul>
11126      * @param {Object} config The config object
11127      */
11128        register : function(config){
11129            var cs = config instanceof Array ? config : arguments;
11130            for(var i = 0, len = cs.length; i < len; i++) {
11131                var c = cs[i];
11132                var target = c.target;
11133                if(target){
11134                    if(target instanceof Array){
11135                        for(var j = 0, jlen = target.length; j < jlen; j++){
11136                            tagEls[target[j]] = c;
11137                        }
11138                    }else{
11139                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11140                    }
11141                }
11142            }
11143        },
11144
11145     /**
11146      * Removes this quick tip from its element and destroys it.
11147      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11148      */
11149        unregister : function(el){
11150            delete tagEls[Roo.id(el)];
11151        },
11152
11153     /**
11154      * Enable this quick tip.
11155      */
11156        enable : function(){
11157            if(inited && disabled){
11158                locks.pop();
11159                if(locks.length < 1){
11160                    disabled = false;
11161                }
11162            }
11163        },
11164
11165     /**
11166      * Disable this quick tip.
11167      */
11168        disable : function(){
11169           disabled = true;
11170           clearTimeout(showProc);
11171           clearTimeout(hideProc);
11172           clearTimeout(dismissProc);
11173           if(ce){
11174               hide(true);
11175           }
11176           locks.push(1);
11177        },
11178
11179     /**
11180      * Returns true if the quick tip is enabled, else false.
11181      */
11182        isEnabled : function(){
11183             return !disabled;
11184        },
11185
11186         // private
11187        tagConfig : {
11188            namespace : "roo", // was ext?? this may break..
11189            alt_namespace : "ext",
11190            attribute : "qtip",
11191            width : "width",
11192            target : "target",
11193            title : "qtitle",
11194            hide : "hide",
11195            cls : "qclass"
11196        }
11197    };
11198 }();
11199
11200 // backwards compat
11201 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211  
11212
11213 /**
11214  * @class Roo.tree.TreePanel
11215  * @extends Roo.data.Tree
11216
11217  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11218  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11219  * @cfg {Boolean} enableDD true to enable drag and drop
11220  * @cfg {Boolean} enableDrag true to enable just drag
11221  * @cfg {Boolean} enableDrop true to enable just drop
11222  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11223  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11224  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11225  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11226  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11227  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11228  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11229  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11230  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11231  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11232  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11233  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11234  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11235  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11236  * @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>
11237  * @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>
11238  * 
11239  * @constructor
11240  * @param {String/HTMLElement/Element} el The container element
11241  * @param {Object} config
11242  */
11243 Roo.tree.TreePanel = function(el, config){
11244     var root = false;
11245     var loader = false;
11246     if (config.root) {
11247         root = config.root;
11248         delete config.root;
11249     }
11250     if (config.loader) {
11251         loader = config.loader;
11252         delete config.loader;
11253     }
11254     
11255     Roo.apply(this, config);
11256     Roo.tree.TreePanel.superclass.constructor.call(this);
11257     this.el = Roo.get(el);
11258     this.el.addClass('x-tree');
11259     //console.log(root);
11260     if (root) {
11261         this.setRootNode( Roo.factory(root, Roo.tree));
11262     }
11263     if (loader) {
11264         this.loader = Roo.factory(loader, Roo.tree);
11265     }
11266    /**
11267     * Read-only. The id of the container element becomes this TreePanel's id.
11268     */
11269     this.id = this.el.id;
11270     this.addEvents({
11271         /**
11272         * @event beforeload
11273         * Fires before a node is loaded, return false to cancel
11274         * @param {Node} node The node being loaded
11275         */
11276         "beforeload" : true,
11277         /**
11278         * @event load
11279         * Fires when a node is loaded
11280         * @param {Node} node The node that was loaded
11281         */
11282         "load" : true,
11283         /**
11284         * @event textchange
11285         * Fires when the text for a node is changed
11286         * @param {Node} node The node
11287         * @param {String} text The new text
11288         * @param {String} oldText The old text
11289         */
11290         "textchange" : true,
11291         /**
11292         * @event beforeexpand
11293         * Fires before a node is expanded, return false to cancel.
11294         * @param {Node} node The node
11295         * @param {Boolean} deep
11296         * @param {Boolean} anim
11297         */
11298         "beforeexpand" : true,
11299         /**
11300         * @event beforecollapse
11301         * Fires before a node is collapsed, return false to cancel.
11302         * @param {Node} node The node
11303         * @param {Boolean} deep
11304         * @param {Boolean} anim
11305         */
11306         "beforecollapse" : true,
11307         /**
11308         * @event expand
11309         * Fires when a node is expanded
11310         * @param {Node} node The node
11311         */
11312         "expand" : true,
11313         /**
11314         * @event disabledchange
11315         * Fires when the disabled status of a node changes
11316         * @param {Node} node The node
11317         * @param {Boolean} disabled
11318         */
11319         "disabledchange" : true,
11320         /**
11321         * @event collapse
11322         * Fires when a node is collapsed
11323         * @param {Node} node The node
11324         */
11325         "collapse" : true,
11326         /**
11327         * @event beforeclick
11328         * Fires before click processing on a node. Return false to cancel the default action.
11329         * @param {Node} node The node
11330         * @param {Roo.EventObject} e The event object
11331         */
11332         "beforeclick":true,
11333         /**
11334         * @event checkchange
11335         * Fires when a node with a checkbox's checked property changes
11336         * @param {Node} this This node
11337         * @param {Boolean} checked
11338         */
11339         "checkchange":true,
11340         /**
11341         * @event click
11342         * Fires when a node is clicked
11343         * @param {Node} node The node
11344         * @param {Roo.EventObject} e The event object
11345         */
11346         "click":true,
11347         /**
11348         * @event dblclick
11349         * Fires when a node is double clicked
11350         * @param {Node} node The node
11351         * @param {Roo.EventObject} e The event object
11352         */
11353         "dblclick":true,
11354         /**
11355         * @event contextmenu
11356         * Fires when a node is right clicked
11357         * @param {Node} node The node
11358         * @param {Roo.EventObject} e The event object
11359         */
11360         "contextmenu":true,
11361         /**
11362         * @event beforechildrenrendered
11363         * Fires right before the child nodes for a node are rendered
11364         * @param {Node} node The node
11365         */
11366         "beforechildrenrendered":true,
11367         /**
11368         * @event startdrag
11369         * Fires when a node starts being dragged
11370         * @param {Roo.tree.TreePanel} this
11371         * @param {Roo.tree.TreeNode} node
11372         * @param {event} e The raw browser event
11373         */ 
11374        "startdrag" : true,
11375        /**
11376         * @event enddrag
11377         * Fires when a drag operation is complete
11378         * @param {Roo.tree.TreePanel} this
11379         * @param {Roo.tree.TreeNode} node
11380         * @param {event} e The raw browser event
11381         */
11382        "enddrag" : true,
11383        /**
11384         * @event dragdrop
11385         * Fires when a dragged node is dropped on a valid DD target
11386         * @param {Roo.tree.TreePanel} this
11387         * @param {Roo.tree.TreeNode} node
11388         * @param {DD} dd The dd it was dropped on
11389         * @param {event} e The raw browser event
11390         */
11391        "dragdrop" : true,
11392        /**
11393         * @event beforenodedrop
11394         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11395         * passed to handlers has the following properties:<br />
11396         * <ul style="padding:5px;padding-left:16px;">
11397         * <li>tree - The TreePanel</li>
11398         * <li>target - The node being targeted for the drop</li>
11399         * <li>data - The drag data from the drag source</li>
11400         * <li>point - The point of the drop - append, above or below</li>
11401         * <li>source - The drag source</li>
11402         * <li>rawEvent - Raw mouse event</li>
11403         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11404         * to be inserted by setting them on this object.</li>
11405         * <li>cancel - Set this to true to cancel the drop.</li>
11406         * </ul>
11407         * @param {Object} dropEvent
11408         */
11409        "beforenodedrop" : true,
11410        /**
11411         * @event nodedrop
11412         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11413         * passed to handlers has the following properties:<br />
11414         * <ul style="padding:5px;padding-left:16px;">
11415         * <li>tree - The TreePanel</li>
11416         * <li>target - The node being targeted for the drop</li>
11417         * <li>data - The drag data from the drag source</li>
11418         * <li>point - The point of the drop - append, above or below</li>
11419         * <li>source - The drag source</li>
11420         * <li>rawEvent - Raw mouse event</li>
11421         * <li>dropNode - Dropped node(s).</li>
11422         * </ul>
11423         * @param {Object} dropEvent
11424         */
11425        "nodedrop" : true,
11426         /**
11427         * @event nodedragover
11428         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11429         * passed to handlers has the following properties:<br />
11430         * <ul style="padding:5px;padding-left:16px;">
11431         * <li>tree - The TreePanel</li>
11432         * <li>target - The node being targeted for the drop</li>
11433         * <li>data - The drag data from the drag source</li>
11434         * <li>point - The point of the drop - append, above or below</li>
11435         * <li>source - The drag source</li>
11436         * <li>rawEvent - Raw mouse event</li>
11437         * <li>dropNode - Drop node(s) provided by the source.</li>
11438         * <li>cancel - Set this to true to signal drop not allowed.</li>
11439         * </ul>
11440         * @param {Object} dragOverEvent
11441         */
11442        "nodedragover" : true,
11443        /**
11444         * @event appendnode
11445         * Fires when append node to the tree
11446         * @param {Roo.tree.TreePanel} this
11447         * @param {Roo.tree.TreeNode} node
11448         * @param {Number} index The index of the newly appended node
11449         */
11450        "appendnode" : true
11451         
11452     });
11453     if(this.singleExpand){
11454        this.on("beforeexpand", this.restrictExpand, this);
11455     }
11456     if (this.editor) {
11457         this.editor.tree = this;
11458         this.editor = Roo.factory(this.editor, Roo.tree);
11459     }
11460     
11461     if (this.selModel) {
11462         this.selModel = Roo.factory(this.selModel, Roo.tree);
11463     }
11464    
11465 };
11466 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11467     rootVisible : true,
11468     animate: Roo.enableFx,
11469     lines : true,
11470     enableDD : false,
11471     hlDrop : Roo.enableFx,
11472   
11473     renderer: false,
11474     
11475     rendererTip: false,
11476     // private
11477     restrictExpand : function(node){
11478         var p = node.parentNode;
11479         if(p){
11480             if(p.expandedChild && p.expandedChild.parentNode == p){
11481                 p.expandedChild.collapse();
11482             }
11483             p.expandedChild = node;
11484         }
11485     },
11486
11487     // private override
11488     setRootNode : function(node){
11489         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11490         if(!this.rootVisible){
11491             node.ui = new Roo.tree.RootTreeNodeUI(node);
11492         }
11493         return node;
11494     },
11495
11496     /**
11497      * Returns the container element for this TreePanel
11498      */
11499     getEl : function(){
11500         return this.el;
11501     },
11502
11503     /**
11504      * Returns the default TreeLoader for this TreePanel
11505      */
11506     getLoader : function(){
11507         return this.loader;
11508     },
11509
11510     /**
11511      * Expand all nodes
11512      */
11513     expandAll : function(){
11514         this.root.expand(true);
11515     },
11516
11517     /**
11518      * Collapse all nodes
11519      */
11520     collapseAll : function(){
11521         this.root.collapse(true);
11522     },
11523
11524     /**
11525      * Returns the selection model used by this TreePanel
11526      */
11527     getSelectionModel : function(){
11528         if(!this.selModel){
11529             this.selModel = new Roo.tree.DefaultSelectionModel();
11530         }
11531         return this.selModel;
11532     },
11533
11534     /**
11535      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11536      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11537      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11538      * @return {Array}
11539      */
11540     getChecked : function(a, startNode){
11541         startNode = startNode || this.root;
11542         var r = [];
11543         var f = function(){
11544             if(this.attributes.checked){
11545                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11546             }
11547         }
11548         startNode.cascade(f);
11549         return r;
11550     },
11551
11552     /**
11553      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11554      * @param {String} path
11555      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11556      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11557      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11558      */
11559     expandPath : function(path, attr, callback){
11560         attr = attr || "id";
11561         var keys = path.split(this.pathSeparator);
11562         var curNode = this.root;
11563         if(curNode.attributes[attr] != keys[1]){ // invalid root
11564             if(callback){
11565                 callback(false, null);
11566             }
11567             return;
11568         }
11569         var index = 1;
11570         var f = function(){
11571             if(++index == keys.length){
11572                 if(callback){
11573                     callback(true, curNode);
11574                 }
11575                 return;
11576             }
11577             var c = curNode.findChild(attr, keys[index]);
11578             if(!c){
11579                 if(callback){
11580                     callback(false, curNode);
11581                 }
11582                 return;
11583             }
11584             curNode = c;
11585             c.expand(false, false, f);
11586         };
11587         curNode.expand(false, false, f);
11588     },
11589
11590     /**
11591      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11592      * @param {String} path
11593      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11594      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11595      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11596      */
11597     selectPath : function(path, attr, callback){
11598         attr = attr || "id";
11599         var keys = path.split(this.pathSeparator);
11600         var v = keys.pop();
11601         if(keys.length > 0){
11602             var f = function(success, node){
11603                 if(success && node){
11604                     var n = node.findChild(attr, v);
11605                     if(n){
11606                         n.select();
11607                         if(callback){
11608                             callback(true, n);
11609                         }
11610                     }else if(callback){
11611                         callback(false, n);
11612                     }
11613                 }else{
11614                     if(callback){
11615                         callback(false, n);
11616                     }
11617                 }
11618             };
11619             this.expandPath(keys.join(this.pathSeparator), attr, f);
11620         }else{
11621             this.root.select();
11622             if(callback){
11623                 callback(true, this.root);
11624             }
11625         }
11626     },
11627
11628     getTreeEl : function(){
11629         return this.el;
11630     },
11631
11632     /**
11633      * Trigger rendering of this TreePanel
11634      */
11635     render : function(){
11636         if (this.innerCt) {
11637             return this; // stop it rendering more than once!!
11638         }
11639         
11640         this.innerCt = this.el.createChild({tag:"ul",
11641                cls:"x-tree-root-ct " +
11642                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11643
11644         if(this.containerScroll){
11645             Roo.dd.ScrollManager.register(this.el);
11646         }
11647         if((this.enableDD || this.enableDrop) && !this.dropZone){
11648            /**
11649             * The dropZone used by this tree if drop is enabled
11650             * @type Roo.tree.TreeDropZone
11651             */
11652              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11653                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11654            });
11655         }
11656         if((this.enableDD || this.enableDrag) && !this.dragZone){
11657            /**
11658             * The dragZone used by this tree if drag is enabled
11659             * @type Roo.tree.TreeDragZone
11660             */
11661             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11662                ddGroup: this.ddGroup || "TreeDD",
11663                scroll: this.ddScroll
11664            });
11665         }
11666         this.getSelectionModel().init(this);
11667         if (!this.root) {
11668             Roo.log("ROOT not set in tree");
11669             return this;
11670         }
11671         this.root.render();
11672         if(!this.rootVisible){
11673             this.root.renderChildren();
11674         }
11675         return this;
11676     }
11677 });/*
11678  * Based on:
11679  * Ext JS Library 1.1.1
11680  * Copyright(c) 2006-2007, Ext JS, LLC.
11681  *
11682  * Originally Released Under LGPL - original licence link has changed is not relivant.
11683  *
11684  * Fork - LGPL
11685  * <script type="text/javascript">
11686  */
11687  
11688
11689 /**
11690  * @class Roo.tree.DefaultSelectionModel
11691  * @extends Roo.util.Observable
11692  * The default single selection for a TreePanel.
11693  * @param {Object} cfg Configuration
11694  */
11695 Roo.tree.DefaultSelectionModel = function(cfg){
11696    this.selNode = null;
11697    
11698    
11699    
11700    this.addEvents({
11701        /**
11702         * @event selectionchange
11703         * Fires when the selected node changes
11704         * @param {DefaultSelectionModel} this
11705         * @param {TreeNode} node the new selection
11706         */
11707        "selectionchange" : true,
11708
11709        /**
11710         * @event beforeselect
11711         * Fires before the selected node changes, return false to cancel the change
11712         * @param {DefaultSelectionModel} this
11713         * @param {TreeNode} node the new selection
11714         * @param {TreeNode} node the old selection
11715         */
11716        "beforeselect" : true
11717    });
11718    
11719     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11720 };
11721
11722 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11723     init : function(tree){
11724         this.tree = tree;
11725         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11726         tree.on("click", this.onNodeClick, this);
11727     },
11728     
11729     onNodeClick : function(node, e){
11730         if (e.ctrlKey && this.selNode == node)  {
11731             this.unselect(node);
11732             return;
11733         }
11734         this.select(node);
11735     },
11736     
11737     /**
11738      * Select a node.
11739      * @param {TreeNode} node The node to select
11740      * @return {TreeNode} The selected node
11741      */
11742     select : function(node){
11743         var last = this.selNode;
11744         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11745             if(last){
11746                 last.ui.onSelectedChange(false);
11747             }
11748             this.selNode = node;
11749             node.ui.onSelectedChange(true);
11750             this.fireEvent("selectionchange", this, node, last);
11751         }
11752         return node;
11753     },
11754     
11755     /**
11756      * Deselect a node.
11757      * @param {TreeNode} node The node to unselect
11758      */
11759     unselect : function(node){
11760         if(this.selNode == node){
11761             this.clearSelections();
11762         }    
11763     },
11764     
11765     /**
11766      * Clear all selections
11767      */
11768     clearSelections : function(){
11769         var n = this.selNode;
11770         if(n){
11771             n.ui.onSelectedChange(false);
11772             this.selNode = null;
11773             this.fireEvent("selectionchange", this, null);
11774         }
11775         return n;
11776     },
11777     
11778     /**
11779      * Get the selected node
11780      * @return {TreeNode} The selected node
11781      */
11782     getSelectedNode : function(){
11783         return this.selNode;    
11784     },
11785     
11786     /**
11787      * Returns true if the node is selected
11788      * @param {TreeNode} node The node to check
11789      * @return {Boolean}
11790      */
11791     isSelected : function(node){
11792         return this.selNode == node;  
11793     },
11794
11795     /**
11796      * Selects the node above the selected node in the tree, intelligently walking the nodes
11797      * @return TreeNode The new selection
11798      */
11799     selectPrevious : function(){
11800         var s = this.selNode || this.lastSelNode;
11801         if(!s){
11802             return null;
11803         }
11804         var ps = s.previousSibling;
11805         if(ps){
11806             if(!ps.isExpanded() || ps.childNodes.length < 1){
11807                 return this.select(ps);
11808             } else{
11809                 var lc = ps.lastChild;
11810                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11811                     lc = lc.lastChild;
11812                 }
11813                 return this.select(lc);
11814             }
11815         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11816             return this.select(s.parentNode);
11817         }
11818         return null;
11819     },
11820
11821     /**
11822      * Selects the node above the selected node in the tree, intelligently walking the nodes
11823      * @return TreeNode The new selection
11824      */
11825     selectNext : function(){
11826         var s = this.selNode || this.lastSelNode;
11827         if(!s){
11828             return null;
11829         }
11830         if(s.firstChild && s.isExpanded()){
11831              return this.select(s.firstChild);
11832          }else if(s.nextSibling){
11833              return this.select(s.nextSibling);
11834          }else if(s.parentNode){
11835             var newS = null;
11836             s.parentNode.bubble(function(){
11837                 if(this.nextSibling){
11838                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11839                     return false;
11840                 }
11841             });
11842             return newS;
11843          }
11844         return null;
11845     },
11846
11847     onKeyDown : function(e){
11848         var s = this.selNode || this.lastSelNode;
11849         // undesirable, but required
11850         var sm = this;
11851         if(!s){
11852             return;
11853         }
11854         var k = e.getKey();
11855         switch(k){
11856              case e.DOWN:
11857                  e.stopEvent();
11858                  this.selectNext();
11859              break;
11860              case e.UP:
11861                  e.stopEvent();
11862                  this.selectPrevious();
11863              break;
11864              case e.RIGHT:
11865                  e.preventDefault();
11866                  if(s.hasChildNodes()){
11867                      if(!s.isExpanded()){
11868                          s.expand();
11869                      }else if(s.firstChild){
11870                          this.select(s.firstChild, e);
11871                      }
11872                  }
11873              break;
11874              case e.LEFT:
11875                  e.preventDefault();
11876                  if(s.hasChildNodes() && s.isExpanded()){
11877                      s.collapse();
11878                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11879                      this.select(s.parentNode, e);
11880                  }
11881              break;
11882         };
11883     }
11884 });
11885
11886 /**
11887  * @class Roo.tree.MultiSelectionModel
11888  * @extends Roo.util.Observable
11889  * Multi selection for a TreePanel.
11890  * @param {Object} cfg Configuration
11891  */
11892 Roo.tree.MultiSelectionModel = function(){
11893    this.selNodes = [];
11894    this.selMap = {};
11895    this.addEvents({
11896        /**
11897         * @event selectionchange
11898         * Fires when the selected nodes change
11899         * @param {MultiSelectionModel} this
11900         * @param {Array} nodes Array of the selected nodes
11901         */
11902        "selectionchange" : true
11903    });
11904    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11905    
11906 };
11907
11908 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11909     init : function(tree){
11910         this.tree = tree;
11911         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11912         tree.on("click", this.onNodeClick, this);
11913     },
11914     
11915     onNodeClick : function(node, e){
11916         this.select(node, e, e.ctrlKey);
11917     },
11918     
11919     /**
11920      * Select a node.
11921      * @param {TreeNode} node The node to select
11922      * @param {EventObject} e (optional) An event associated with the selection
11923      * @param {Boolean} keepExisting True to retain existing selections
11924      * @return {TreeNode} The selected node
11925      */
11926     select : function(node, e, keepExisting){
11927         if(keepExisting !== true){
11928             this.clearSelections(true);
11929         }
11930         if(this.isSelected(node)){
11931             this.lastSelNode = node;
11932             return node;
11933         }
11934         this.selNodes.push(node);
11935         this.selMap[node.id] = node;
11936         this.lastSelNode = node;
11937         node.ui.onSelectedChange(true);
11938         this.fireEvent("selectionchange", this, this.selNodes);
11939         return node;
11940     },
11941     
11942     /**
11943      * Deselect a node.
11944      * @param {TreeNode} node The node to unselect
11945      */
11946     unselect : function(node){
11947         if(this.selMap[node.id]){
11948             node.ui.onSelectedChange(false);
11949             var sn = this.selNodes;
11950             var index = -1;
11951             if(sn.indexOf){
11952                 index = sn.indexOf(node);
11953             }else{
11954                 for(var i = 0, len = sn.length; i < len; i++){
11955                     if(sn[i] == node){
11956                         index = i;
11957                         break;
11958                     }
11959                 }
11960             }
11961             if(index != -1){
11962                 this.selNodes.splice(index, 1);
11963             }
11964             delete this.selMap[node.id];
11965             this.fireEvent("selectionchange", this, this.selNodes);
11966         }
11967     },
11968     
11969     /**
11970      * Clear all selections
11971      */
11972     clearSelections : function(suppressEvent){
11973         var sn = this.selNodes;
11974         if(sn.length > 0){
11975             for(var i = 0, len = sn.length; i < len; i++){
11976                 sn[i].ui.onSelectedChange(false);
11977             }
11978             this.selNodes = [];
11979             this.selMap = {};
11980             if(suppressEvent !== true){
11981                 this.fireEvent("selectionchange", this, this.selNodes);
11982             }
11983         }
11984     },
11985     
11986     /**
11987      * Returns true if the node is selected
11988      * @param {TreeNode} node The node to check
11989      * @return {Boolean}
11990      */
11991     isSelected : function(node){
11992         return this.selMap[node.id] ? true : false;  
11993     },
11994     
11995     /**
11996      * Returns an array of the selected nodes
11997      * @return {Array}
11998      */
11999     getSelectedNodes : function(){
12000         return this.selNodes;    
12001     },
12002
12003     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12004
12005     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12006
12007     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12008 });/*
12009  * Based on:
12010  * Ext JS Library 1.1.1
12011  * Copyright(c) 2006-2007, Ext JS, LLC.
12012  *
12013  * Originally Released Under LGPL - original licence link has changed is not relivant.
12014  *
12015  * Fork - LGPL
12016  * <script type="text/javascript">
12017  */
12018  
12019 /**
12020  * @class Roo.tree.TreeNode
12021  * @extends Roo.data.Node
12022  * @cfg {String} text The text for this node
12023  * @cfg {Boolean} expanded true to start the node expanded
12024  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12025  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12026  * @cfg {Boolean} disabled true to start the node disabled
12027  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12028  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12029  * @cfg {String} cls A css class to be added to the node
12030  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12031  * @cfg {String} href URL of the link used for the node (defaults to #)
12032  * @cfg {String} hrefTarget target frame for the link
12033  * @cfg {String} qtip An Ext QuickTip for the node
12034  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12035  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12036  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12037  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12038  * (defaults to undefined with no checkbox rendered)
12039  * @constructor
12040  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12041  */
12042 Roo.tree.TreeNode = function(attributes){
12043     attributes = attributes || {};
12044     if(typeof attributes == "string"){
12045         attributes = {text: attributes};
12046     }
12047     this.childrenRendered = false;
12048     this.rendered = false;
12049     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12050     this.expanded = attributes.expanded === true;
12051     this.isTarget = attributes.isTarget !== false;
12052     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12053     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12054
12055     /**
12056      * Read-only. The text for this node. To change it use setText().
12057      * @type String
12058      */
12059     this.text = attributes.text;
12060     /**
12061      * True if this node is disabled.
12062      * @type Boolean
12063      */
12064     this.disabled = attributes.disabled === true;
12065
12066     this.addEvents({
12067         /**
12068         * @event textchange
12069         * Fires when the text for this node is changed
12070         * @param {Node} this This node
12071         * @param {String} text The new text
12072         * @param {String} oldText The old text
12073         */
12074         "textchange" : true,
12075         /**
12076         * @event beforeexpand
12077         * Fires before this node is expanded, return false to cancel.
12078         * @param {Node} this This node
12079         * @param {Boolean} deep
12080         * @param {Boolean} anim
12081         */
12082         "beforeexpand" : true,
12083         /**
12084         * @event beforecollapse
12085         * Fires before this node is collapsed, return false to cancel.
12086         * @param {Node} this This node
12087         * @param {Boolean} deep
12088         * @param {Boolean} anim
12089         */
12090         "beforecollapse" : true,
12091         /**
12092         * @event expand
12093         * Fires when this node is expanded
12094         * @param {Node} this This node
12095         */
12096         "expand" : true,
12097         /**
12098         * @event disabledchange
12099         * Fires when the disabled status of this node changes
12100         * @param {Node} this This node
12101         * @param {Boolean} disabled
12102         */
12103         "disabledchange" : true,
12104         /**
12105         * @event collapse
12106         * Fires when this node is collapsed
12107         * @param {Node} this This node
12108         */
12109         "collapse" : true,
12110         /**
12111         * @event beforeclick
12112         * Fires before click processing. Return false to cancel the default action.
12113         * @param {Node} this This node
12114         * @param {Roo.EventObject} e The event object
12115         */
12116         "beforeclick":true,
12117         /**
12118         * @event checkchange
12119         * Fires when a node with a checkbox's checked property changes
12120         * @param {Node} this This node
12121         * @param {Boolean} checked
12122         */
12123         "checkchange":true,
12124         /**
12125         * @event click
12126         * Fires when this node is clicked
12127         * @param {Node} this This node
12128         * @param {Roo.EventObject} e The event object
12129         */
12130         "click":true,
12131         /**
12132         * @event dblclick
12133         * Fires when this node is double clicked
12134         * @param {Node} this This node
12135         * @param {Roo.EventObject} e The event object
12136         */
12137         "dblclick":true,
12138         /**
12139         * @event contextmenu
12140         * Fires when this node is right clicked
12141         * @param {Node} this This node
12142         * @param {Roo.EventObject} e The event object
12143         */
12144         "contextmenu":true,
12145         /**
12146         * @event beforechildrenrendered
12147         * Fires right before the child nodes for this node are rendered
12148         * @param {Node} this This node
12149         */
12150         "beforechildrenrendered":true
12151     });
12152
12153     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12154
12155     /**
12156      * Read-only. The UI for this node
12157      * @type TreeNodeUI
12158      */
12159     this.ui = new uiClass(this);
12160     
12161     // finally support items[]
12162     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12163         return;
12164     }
12165     
12166     
12167     Roo.each(this.attributes.items, function(c) {
12168         this.appendChild(Roo.factory(c,Roo.Tree));
12169     }, this);
12170     delete this.attributes.items;
12171     
12172     
12173     
12174 };
12175 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12176     preventHScroll: true,
12177     /**
12178      * Returns true if this node is expanded
12179      * @return {Boolean}
12180      */
12181     isExpanded : function(){
12182         return this.expanded;
12183     },
12184
12185     /**
12186      * Returns the UI object for this node
12187      * @return {TreeNodeUI}
12188      */
12189     getUI : function(){
12190         return this.ui;
12191     },
12192
12193     // private override
12194     setFirstChild : function(node){
12195         var of = this.firstChild;
12196         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12197         if(this.childrenRendered && of && node != of){
12198             of.renderIndent(true, true);
12199         }
12200         if(this.rendered){
12201             this.renderIndent(true, true);
12202         }
12203     },
12204
12205     // private override
12206     setLastChild : function(node){
12207         var ol = this.lastChild;
12208         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12209         if(this.childrenRendered && ol && node != ol){
12210             ol.renderIndent(true, true);
12211         }
12212         if(this.rendered){
12213             this.renderIndent(true, true);
12214         }
12215     },
12216
12217     // these methods are overridden to provide lazy rendering support
12218     // private override
12219     appendChild : function()
12220     {
12221         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12222         if(node && this.childrenRendered){
12223             node.render();
12224         }
12225         this.ui.updateExpandIcon();
12226         return node;
12227     },
12228
12229     // private override
12230     removeChild : function(node){
12231         this.ownerTree.getSelectionModel().unselect(node);
12232         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12233         // if it's been rendered remove dom node
12234         if(this.childrenRendered){
12235             node.ui.remove();
12236         }
12237         if(this.childNodes.length < 1){
12238             this.collapse(false, false);
12239         }else{
12240             this.ui.updateExpandIcon();
12241         }
12242         if(!this.firstChild) {
12243             this.childrenRendered = false;
12244         }
12245         return node;
12246     },
12247
12248     // private override
12249     insertBefore : function(node, refNode){
12250         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12251         if(newNode && refNode && this.childrenRendered){
12252             node.render();
12253         }
12254         this.ui.updateExpandIcon();
12255         return newNode;
12256     },
12257
12258     /**
12259      * Sets the text for this node
12260      * @param {String} text
12261      */
12262     setText : function(text){
12263         var oldText = this.text;
12264         this.text = text;
12265         this.attributes.text = text;
12266         if(this.rendered){ // event without subscribing
12267             this.ui.onTextChange(this, text, oldText);
12268         }
12269         this.fireEvent("textchange", this, text, oldText);
12270     },
12271
12272     /**
12273      * Triggers selection of this node
12274      */
12275     select : function(){
12276         this.getOwnerTree().getSelectionModel().select(this);
12277     },
12278
12279     /**
12280      * Triggers deselection of this node
12281      */
12282     unselect : function(){
12283         this.getOwnerTree().getSelectionModel().unselect(this);
12284     },
12285
12286     /**
12287      * Returns true if this node is selected
12288      * @return {Boolean}
12289      */
12290     isSelected : function(){
12291         return this.getOwnerTree().getSelectionModel().isSelected(this);
12292     },
12293
12294     /**
12295      * Expand this node.
12296      * @param {Boolean} deep (optional) True to expand all children as well
12297      * @param {Boolean} anim (optional) false to cancel the default animation
12298      * @param {Function} callback (optional) A callback to be called when
12299      * expanding this node completes (does not wait for deep expand to complete).
12300      * Called with 1 parameter, this node.
12301      */
12302     expand : function(deep, anim, callback){
12303         if(!this.expanded){
12304             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12305                 return;
12306             }
12307             if(!this.childrenRendered){
12308                 this.renderChildren();
12309             }
12310             this.expanded = true;
12311             
12312             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12313                 this.ui.animExpand(function(){
12314                     this.fireEvent("expand", this);
12315                     if(typeof callback == "function"){
12316                         callback(this);
12317                     }
12318                     if(deep === true){
12319                         this.expandChildNodes(true);
12320                     }
12321                 }.createDelegate(this));
12322                 return;
12323             }else{
12324                 this.ui.expand();
12325                 this.fireEvent("expand", this);
12326                 if(typeof callback == "function"){
12327                     callback(this);
12328                 }
12329             }
12330         }else{
12331            if(typeof callback == "function"){
12332                callback(this);
12333            }
12334         }
12335         if(deep === true){
12336             this.expandChildNodes(true);
12337         }
12338     },
12339
12340     isHiddenRoot : function(){
12341         return this.isRoot && !this.getOwnerTree().rootVisible;
12342     },
12343
12344     /**
12345      * Collapse this node.
12346      * @param {Boolean} deep (optional) True to collapse all children as well
12347      * @param {Boolean} anim (optional) false to cancel the default animation
12348      */
12349     collapse : function(deep, anim){
12350         if(this.expanded && !this.isHiddenRoot()){
12351             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12352                 return;
12353             }
12354             this.expanded = false;
12355             if((this.getOwnerTree().animate && anim !== false) || anim){
12356                 this.ui.animCollapse(function(){
12357                     this.fireEvent("collapse", this);
12358                     if(deep === true){
12359                         this.collapseChildNodes(true);
12360                     }
12361                 }.createDelegate(this));
12362                 return;
12363             }else{
12364                 this.ui.collapse();
12365                 this.fireEvent("collapse", this);
12366             }
12367         }
12368         if(deep === true){
12369             var cs = this.childNodes;
12370             for(var i = 0, len = cs.length; i < len; i++) {
12371                 cs[i].collapse(true, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     delayedExpand : function(delay){
12378         if(!this.expandProcId){
12379             this.expandProcId = this.expand.defer(delay, this);
12380         }
12381     },
12382
12383     // private
12384     cancelExpand : function(){
12385         if(this.expandProcId){
12386             clearTimeout(this.expandProcId);
12387         }
12388         this.expandProcId = false;
12389     },
12390
12391     /**
12392      * Toggles expanded/collapsed state of the node
12393      */
12394     toggle : function(){
12395         if(this.expanded){
12396             this.collapse();
12397         }else{
12398             this.expand();
12399         }
12400     },
12401
12402     /**
12403      * Ensures all parent nodes are expanded
12404      */
12405     ensureVisible : function(callback){
12406         var tree = this.getOwnerTree();
12407         tree.expandPath(this.parentNode.getPath(), false, function(){
12408             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12409             Roo.callback(callback);
12410         }.createDelegate(this));
12411     },
12412
12413     /**
12414      * Expand all child nodes
12415      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12416      */
12417     expandChildNodes : function(deep){
12418         var cs = this.childNodes;
12419         for(var i = 0, len = cs.length; i < len; i++) {
12420                 cs[i].expand(deep);
12421         }
12422     },
12423
12424     /**
12425      * Collapse all child nodes
12426      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12427      */
12428     collapseChildNodes : function(deep){
12429         var cs = this.childNodes;
12430         for(var i = 0, len = cs.length; i < len; i++) {
12431                 cs[i].collapse(deep);
12432         }
12433     },
12434
12435     /**
12436      * Disables this node
12437      */
12438     disable : function(){
12439         this.disabled = true;
12440         this.unselect();
12441         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12442             this.ui.onDisableChange(this, true);
12443         }
12444         this.fireEvent("disabledchange", this, true);
12445     },
12446
12447     /**
12448      * Enables this node
12449      */
12450     enable : function(){
12451         this.disabled = false;
12452         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12453             this.ui.onDisableChange(this, false);
12454         }
12455         this.fireEvent("disabledchange", this, false);
12456     },
12457
12458     // private
12459     renderChildren : function(suppressEvent){
12460         if(suppressEvent !== false){
12461             this.fireEvent("beforechildrenrendered", this);
12462         }
12463         var cs = this.childNodes;
12464         for(var i = 0, len = cs.length; i < len; i++){
12465             cs[i].render(true);
12466         }
12467         this.childrenRendered = true;
12468     },
12469
12470     // private
12471     sort : function(fn, scope){
12472         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12473         if(this.childrenRendered){
12474             var cs = this.childNodes;
12475             for(var i = 0, len = cs.length; i < len; i++){
12476                 cs[i].render(true);
12477             }
12478         }
12479     },
12480
12481     // private
12482     render : function(bulkRender){
12483         this.ui.render(bulkRender);
12484         if(!this.rendered){
12485             this.rendered = true;
12486             if(this.expanded){
12487                 this.expanded = false;
12488                 this.expand(false, false);
12489             }
12490         }
12491     },
12492
12493     // private
12494     renderIndent : function(deep, refresh){
12495         if(refresh){
12496             this.ui.childIndent = null;
12497         }
12498         this.ui.renderIndent();
12499         if(deep === true && this.childrenRendered){
12500             var cs = this.childNodes;
12501             for(var i = 0, len = cs.length; i < len; i++){
12502                 cs[i].renderIndent(true, refresh);
12503             }
12504         }
12505     }
12506 });/*
12507  * Based on:
12508  * Ext JS Library 1.1.1
12509  * Copyright(c) 2006-2007, Ext JS, LLC.
12510  *
12511  * Originally Released Under LGPL - original licence link has changed is not relivant.
12512  *
12513  * Fork - LGPL
12514  * <script type="text/javascript">
12515  */
12516  
12517 /**
12518  * @class Roo.tree.AsyncTreeNode
12519  * @extends Roo.tree.TreeNode
12520  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12521  * @constructor
12522  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12523  */
12524  Roo.tree.AsyncTreeNode = function(config){
12525     this.loaded = false;
12526     this.loading = false;
12527     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12528     /**
12529     * @event beforeload
12530     * Fires before this node is loaded, return false to cancel
12531     * @param {Node} this This node
12532     */
12533     this.addEvents({'beforeload':true, 'load': true});
12534     /**
12535     * @event load
12536     * Fires when this node is loaded
12537     * @param {Node} this This node
12538     */
12539     /**
12540      * The loader used by this node (defaults to using the tree's defined loader)
12541      * @type TreeLoader
12542      * @property loader
12543      */
12544 };
12545 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12546     expand : function(deep, anim, callback){
12547         if(this.loading){ // if an async load is already running, waiting til it's done
12548             var timer;
12549             var f = function(){
12550                 if(!this.loading){ // done loading
12551                     clearInterval(timer);
12552                     this.expand(deep, anim, callback);
12553                 }
12554             }.createDelegate(this);
12555             timer = setInterval(f, 200);
12556             return;
12557         }
12558         if(!this.loaded){
12559             if(this.fireEvent("beforeload", this) === false){
12560                 return;
12561             }
12562             this.loading = true;
12563             this.ui.beforeLoad(this);
12564             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12565             if(loader){
12566                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12567                 return;
12568             }
12569         }
12570         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12571     },
12572     
12573     /**
12574      * Returns true if this node is currently loading
12575      * @return {Boolean}
12576      */
12577     isLoading : function(){
12578         return this.loading;  
12579     },
12580     
12581     loadComplete : function(deep, anim, callback){
12582         this.loading = false;
12583         this.loaded = true;
12584         this.ui.afterLoad(this);
12585         this.fireEvent("load", this);
12586         this.expand(deep, anim, callback);
12587     },
12588     
12589     /**
12590      * Returns true if this node has been loaded
12591      * @return {Boolean}
12592      */
12593     isLoaded : function(){
12594         return this.loaded;
12595     },
12596     
12597     hasChildNodes : function(){
12598         if(!this.isLeaf() && !this.loaded){
12599             return true;
12600         }else{
12601             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12602         }
12603     },
12604
12605     /**
12606      * Trigger a reload for this node
12607      * @param {Function} callback
12608      */
12609     reload : function(callback){
12610         this.collapse(false, false);
12611         while(this.firstChild){
12612             this.removeChild(this.firstChild);
12613         }
12614         this.childrenRendered = false;
12615         this.loaded = false;
12616         if(this.isHiddenRoot()){
12617             this.expanded = false;
12618         }
12619         this.expand(false, false, callback);
12620     }
12621 });/*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631  
12632 /**
12633  * @class Roo.tree.TreeNodeUI
12634  * @constructor
12635  * @param {Object} node The node to render
12636  * The TreeNode UI implementation is separate from the
12637  * tree implementation. Unless you are customizing the tree UI,
12638  * you should never have to use this directly.
12639  */
12640 Roo.tree.TreeNodeUI = function(node){
12641     this.node = node;
12642     this.rendered = false;
12643     this.animating = false;
12644     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12645 };
12646
12647 Roo.tree.TreeNodeUI.prototype = {
12648     removeChild : function(node){
12649         if(this.rendered){
12650             this.ctNode.removeChild(node.ui.getEl());
12651         }
12652     },
12653
12654     beforeLoad : function(){
12655          this.addClass("x-tree-node-loading");
12656     },
12657
12658     afterLoad : function(){
12659          this.removeClass("x-tree-node-loading");
12660     },
12661
12662     onTextChange : function(node, text, oldText){
12663         if(this.rendered){
12664             this.textNode.innerHTML = text;
12665         }
12666     },
12667
12668     onDisableChange : function(node, state){
12669         this.disabled = state;
12670         if(state){
12671             this.addClass("x-tree-node-disabled");
12672         }else{
12673             this.removeClass("x-tree-node-disabled");
12674         }
12675     },
12676
12677     onSelectedChange : function(state){
12678         if(state){
12679             this.focus();
12680             this.addClass("x-tree-selected");
12681         }else{
12682             //this.blur();
12683             this.removeClass("x-tree-selected");
12684         }
12685     },
12686
12687     onMove : function(tree, node, oldParent, newParent, index, refNode){
12688         this.childIndent = null;
12689         if(this.rendered){
12690             var targetNode = newParent.ui.getContainer();
12691             if(!targetNode){//target not rendered
12692                 this.holder = document.createElement("div");
12693                 this.holder.appendChild(this.wrap);
12694                 return;
12695             }
12696             var insertBefore = refNode ? refNode.ui.getEl() : null;
12697             if(insertBefore){
12698                 targetNode.insertBefore(this.wrap, insertBefore);
12699             }else{
12700                 targetNode.appendChild(this.wrap);
12701             }
12702             this.node.renderIndent(true);
12703         }
12704     },
12705
12706     addClass : function(cls){
12707         if(this.elNode){
12708             Roo.fly(this.elNode).addClass(cls);
12709         }
12710     },
12711
12712     removeClass : function(cls){
12713         if(this.elNode){
12714             Roo.fly(this.elNode).removeClass(cls);
12715         }
12716     },
12717
12718     remove : function(){
12719         if(this.rendered){
12720             this.holder = document.createElement("div");
12721             this.holder.appendChild(this.wrap);
12722         }
12723     },
12724
12725     fireEvent : function(){
12726         return this.node.fireEvent.apply(this.node, arguments);
12727     },
12728
12729     initEvents : function(){
12730         this.node.on("move", this.onMove, this);
12731         var E = Roo.EventManager;
12732         var a = this.anchor;
12733
12734         var el = Roo.fly(a, '_treeui');
12735
12736         if(Roo.isOpera){ // opera render bug ignores the CSS
12737             el.setStyle("text-decoration", "none");
12738         }
12739
12740         el.on("click", this.onClick, this);
12741         el.on("dblclick", this.onDblClick, this);
12742
12743         if(this.checkbox){
12744             Roo.EventManager.on(this.checkbox,
12745                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12746         }
12747
12748         el.on("contextmenu", this.onContextMenu, this);
12749
12750         var icon = Roo.fly(this.iconNode);
12751         icon.on("click", this.onClick, this);
12752         icon.on("dblclick", this.onDblClick, this);
12753         icon.on("contextmenu", this.onContextMenu, this);
12754         E.on(this.ecNode, "click", this.ecClick, this, true);
12755
12756         if(this.node.disabled){
12757             this.addClass("x-tree-node-disabled");
12758         }
12759         if(this.node.hidden){
12760             this.addClass("x-tree-node-disabled");
12761         }
12762         var ot = this.node.getOwnerTree();
12763         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12764         if(dd && (!this.node.isRoot || ot.rootVisible)){
12765             Roo.dd.Registry.register(this.elNode, {
12766                 node: this.node,
12767                 handles: this.getDDHandles(),
12768                 isHandle: false
12769             });
12770         }
12771     },
12772
12773     getDDHandles : function(){
12774         return [this.iconNode, this.textNode];
12775     },
12776
12777     hide : function(){
12778         if(this.rendered){
12779             this.wrap.style.display = "none";
12780         }
12781     },
12782
12783     show : function(){
12784         if(this.rendered){
12785             this.wrap.style.display = "";
12786         }
12787     },
12788
12789     onContextMenu : function(e){
12790         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12791             e.preventDefault();
12792             this.focus();
12793             this.fireEvent("contextmenu", this.node, e);
12794         }
12795     },
12796
12797     onClick : function(e){
12798         if(this.dropping){
12799             e.stopEvent();
12800             return;
12801         }
12802         if(this.fireEvent("beforeclick", this.node, e) !== false){
12803             if(!this.disabled && this.node.attributes.href){
12804                 this.fireEvent("click", this.node, e);
12805                 return;
12806             }
12807             e.preventDefault();
12808             if(this.disabled){
12809                 return;
12810             }
12811
12812             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12813                 this.node.toggle();
12814             }
12815
12816             this.fireEvent("click", this.node, e);
12817         }else{
12818             e.stopEvent();
12819         }
12820     },
12821
12822     onDblClick : function(e){
12823         e.preventDefault();
12824         if(this.disabled){
12825             return;
12826         }
12827         if(this.checkbox){
12828             this.toggleCheck();
12829         }
12830         if(!this.animating && this.node.hasChildNodes()){
12831             this.node.toggle();
12832         }
12833         this.fireEvent("dblclick", this.node, e);
12834     },
12835
12836     onCheckChange : function(){
12837         var checked = this.checkbox.checked;
12838         this.node.attributes.checked = checked;
12839         this.fireEvent('checkchange', this.node, checked);
12840     },
12841
12842     ecClick : function(e){
12843         if(!this.animating && this.node.hasChildNodes()){
12844             this.node.toggle();
12845         }
12846     },
12847
12848     startDrop : function(){
12849         this.dropping = true;
12850     },
12851
12852     // delayed drop so the click event doesn't get fired on a drop
12853     endDrop : function(){
12854        setTimeout(function(){
12855            this.dropping = false;
12856        }.createDelegate(this), 50);
12857     },
12858
12859     expand : function(){
12860         this.updateExpandIcon();
12861         this.ctNode.style.display = "";
12862     },
12863
12864     focus : function(){
12865         if(!this.node.preventHScroll){
12866             try{this.anchor.focus();
12867             }catch(e){}
12868         }else if(!Roo.isIE){
12869             try{
12870                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12871                 var l = noscroll.scrollLeft;
12872                 this.anchor.focus();
12873                 noscroll.scrollLeft = l;
12874             }catch(e){}
12875         }
12876     },
12877
12878     toggleCheck : function(value){
12879         var cb = this.checkbox;
12880         if(cb){
12881             cb.checked = (value === undefined ? !cb.checked : value);
12882         }
12883     },
12884
12885     blur : function(){
12886         try{
12887             this.anchor.blur();
12888         }catch(e){}
12889     },
12890
12891     animExpand : function(callback){
12892         var ct = Roo.get(this.ctNode);
12893         ct.stopFx();
12894         if(!this.node.hasChildNodes()){
12895             this.updateExpandIcon();
12896             this.ctNode.style.display = "";
12897             Roo.callback(callback);
12898             return;
12899         }
12900         this.animating = true;
12901         this.updateExpandIcon();
12902
12903         ct.slideIn('t', {
12904            callback : function(){
12905                this.animating = false;
12906                Roo.callback(callback);
12907             },
12908             scope: this,
12909             duration: this.node.ownerTree.duration || .25
12910         });
12911     },
12912
12913     highlight : function(){
12914         var tree = this.node.getOwnerTree();
12915         Roo.fly(this.wrap).highlight(
12916             tree.hlColor || "C3DAF9",
12917             {endColor: tree.hlBaseColor}
12918         );
12919     },
12920
12921     collapse : function(){
12922         this.updateExpandIcon();
12923         this.ctNode.style.display = "none";
12924     },
12925
12926     animCollapse : function(callback){
12927         var ct = Roo.get(this.ctNode);
12928         ct.enableDisplayMode('block');
12929         ct.stopFx();
12930
12931         this.animating = true;
12932         this.updateExpandIcon();
12933
12934         ct.slideOut('t', {
12935             callback : function(){
12936                this.animating = false;
12937                Roo.callback(callback);
12938             },
12939             scope: this,
12940             duration: this.node.ownerTree.duration || .25
12941         });
12942     },
12943
12944     getContainer : function(){
12945         return this.ctNode;
12946     },
12947
12948     getEl : function(){
12949         return this.wrap;
12950     },
12951
12952     appendDDGhost : function(ghostNode){
12953         ghostNode.appendChild(this.elNode.cloneNode(true));
12954     },
12955
12956     getDDRepairXY : function(){
12957         return Roo.lib.Dom.getXY(this.iconNode);
12958     },
12959
12960     onRender : function(){
12961         this.render();
12962     },
12963
12964     render : function(bulkRender){
12965         var n = this.node, a = n.attributes;
12966         var targetNode = n.parentNode ?
12967               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12968
12969         if(!this.rendered){
12970             this.rendered = true;
12971
12972             this.renderElements(n, a, targetNode, bulkRender);
12973
12974             if(a.qtip){
12975                if(this.textNode.setAttributeNS){
12976                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12977                    if(a.qtipTitle){
12978                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12979                    }
12980                }else{
12981                    this.textNode.setAttribute("ext:qtip", a.qtip);
12982                    if(a.qtipTitle){
12983                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12984                    }
12985                }
12986             }else if(a.qtipCfg){
12987                 a.qtipCfg.target = Roo.id(this.textNode);
12988                 Roo.QuickTips.register(a.qtipCfg);
12989             }
12990             this.initEvents();
12991             if(!this.node.expanded){
12992                 this.updateExpandIcon();
12993             }
12994         }else{
12995             if(bulkRender === true) {
12996                 targetNode.appendChild(this.wrap);
12997             }
12998         }
12999     },
13000
13001     renderElements : function(n, a, targetNode, bulkRender)
13002     {
13003         // add some indent caching, this helps performance when rendering a large tree
13004         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13005         var t = n.getOwnerTree();
13006         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13007         if (typeof(n.attributes.html) != 'undefined') {
13008             txt = n.attributes.html;
13009         }
13010         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13011         var cb = typeof a.checked == 'boolean';
13012         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13013         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13014             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13015             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13016             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13017             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13018             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13019              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13020                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13021             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13022             "</li>"];
13023
13024         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13025             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13026                                 n.nextSibling.ui.getEl(), buf.join(""));
13027         }else{
13028             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13029         }
13030
13031         this.elNode = this.wrap.childNodes[0];
13032         this.ctNode = this.wrap.childNodes[1];
13033         var cs = this.elNode.childNodes;
13034         this.indentNode = cs[0];
13035         this.ecNode = cs[1];
13036         this.iconNode = cs[2];
13037         var index = 3;
13038         if(cb){
13039             this.checkbox = cs[3];
13040             index++;
13041         }
13042         this.anchor = cs[index];
13043         this.textNode = cs[index].firstChild;
13044     },
13045
13046     getAnchor : function(){
13047         return this.anchor;
13048     },
13049
13050     getTextEl : function(){
13051         return this.textNode;
13052     },
13053
13054     getIconEl : function(){
13055         return this.iconNode;
13056     },
13057
13058     isChecked : function(){
13059         return this.checkbox ? this.checkbox.checked : false;
13060     },
13061
13062     updateExpandIcon : function(){
13063         if(this.rendered){
13064             var n = this.node, c1, c2;
13065             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13066             var hasChild = n.hasChildNodes();
13067             if(hasChild){
13068                 if(n.expanded){
13069                     cls += "-minus";
13070                     c1 = "x-tree-node-collapsed";
13071                     c2 = "x-tree-node-expanded";
13072                 }else{
13073                     cls += "-plus";
13074                     c1 = "x-tree-node-expanded";
13075                     c2 = "x-tree-node-collapsed";
13076                 }
13077                 if(this.wasLeaf){
13078                     this.removeClass("x-tree-node-leaf");
13079                     this.wasLeaf = false;
13080                 }
13081                 if(this.c1 != c1 || this.c2 != c2){
13082                     Roo.fly(this.elNode).replaceClass(c1, c2);
13083                     this.c1 = c1; this.c2 = c2;
13084                 }
13085             }else{
13086                 // this changes non-leafs into leafs if they have no children.
13087                 // it's not very rational behaviour..
13088                 
13089                 if(!this.wasLeaf && this.node.leaf){
13090                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13091                     delete this.c1;
13092                     delete this.c2;
13093                     this.wasLeaf = true;
13094                 }
13095             }
13096             var ecc = "x-tree-ec-icon "+cls;
13097             if(this.ecc != ecc){
13098                 this.ecNode.className = ecc;
13099                 this.ecc = ecc;
13100             }
13101         }
13102     },
13103
13104     getChildIndent : function(){
13105         if(!this.childIndent){
13106             var buf = [];
13107             var p = this.node;
13108             while(p){
13109                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13110                     if(!p.isLast()) {
13111                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13112                     } else {
13113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13114                     }
13115                 }
13116                 p = p.parentNode;
13117             }
13118             this.childIndent = buf.join("");
13119         }
13120         return this.childIndent;
13121     },
13122
13123     renderIndent : function(){
13124         if(this.rendered){
13125             var indent = "";
13126             var p = this.node.parentNode;
13127             if(p){
13128                 indent = p.ui.getChildIndent();
13129             }
13130             if(this.indentMarkup != indent){ // don't rerender if not required
13131                 this.indentNode.innerHTML = indent;
13132                 this.indentMarkup = indent;
13133             }
13134             this.updateExpandIcon();
13135         }
13136     }
13137 };
13138
13139 Roo.tree.RootTreeNodeUI = function(){
13140     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13141 };
13142 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13143     render : function(){
13144         if(!this.rendered){
13145             var targetNode = this.node.ownerTree.innerCt.dom;
13146             this.node.expanded = true;
13147             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13148             this.wrap = this.ctNode = targetNode.firstChild;
13149         }
13150     },
13151     collapse : function(){
13152     },
13153     expand : function(){
13154     }
13155 });/*
13156  * Based on:
13157  * Ext JS Library 1.1.1
13158  * Copyright(c) 2006-2007, Ext JS, LLC.
13159  *
13160  * Originally Released Under LGPL - original licence link has changed is not relivant.
13161  *
13162  * Fork - LGPL
13163  * <script type="text/javascript">
13164  */
13165 /**
13166  * @class Roo.tree.TreeLoader
13167  * @extends Roo.util.Observable
13168  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13169  * nodes from a specified URL. The response must be a javascript Array definition
13170  * who's elements are node definition objects. eg:
13171  * <pre><code>
13172 {  success : true,
13173    data :      [
13174    
13175     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13176     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13177     ]
13178 }
13179
13180
13181 </code></pre>
13182  * <br><br>
13183  * The old style respose with just an array is still supported, but not recommended.
13184  * <br><br>
13185  *
13186  * A server request is sent, and child nodes are loaded only when a node is expanded.
13187  * The loading node's id is passed to the server under the parameter name "node" to
13188  * enable the server to produce the correct child nodes.
13189  * <br><br>
13190  * To pass extra parameters, an event handler may be attached to the "beforeload"
13191  * event, and the parameters specified in the TreeLoader's baseParams property:
13192  * <pre><code>
13193     myTreeLoader.on("beforeload", function(treeLoader, node) {
13194         this.baseParams.category = node.attributes.category;
13195     }, this);
13196     
13197 </code></pre>
13198  *
13199  * This would pass an HTTP parameter called "category" to the server containing
13200  * the value of the Node's "category" attribute.
13201  * @constructor
13202  * Creates a new Treeloader.
13203  * @param {Object} config A config object containing config properties.
13204  */
13205 Roo.tree.TreeLoader = function(config){
13206     this.baseParams = {};
13207     this.requestMethod = "POST";
13208     Roo.apply(this, config);
13209
13210     this.addEvents({
13211     
13212         /**
13213          * @event beforeload
13214          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13215          * @param {Object} This TreeLoader object.
13216          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13217          * @param {Object} callback The callback function specified in the {@link #load} call.
13218          */
13219         beforeload : true,
13220         /**
13221          * @event load
13222          * Fires when the node has been successfuly loaded.
13223          * @param {Object} This TreeLoader object.
13224          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13225          * @param {Object} response The response object containing the data from the server.
13226          */
13227         load : true,
13228         /**
13229          * @event loadexception
13230          * Fires if the network request failed.
13231          * @param {Object} This TreeLoader object.
13232          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13233          * @param {Object} response The response object containing the data from the server.
13234          */
13235         loadexception : true,
13236         /**
13237          * @event create
13238          * Fires before a node is created, enabling you to return custom Node types 
13239          * @param {Object} This TreeLoader object.
13240          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13241          */
13242         create : true
13243     });
13244
13245     Roo.tree.TreeLoader.superclass.constructor.call(this);
13246 };
13247
13248 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13249     /**
13250     * @cfg {String} dataUrl The URL from which to request a Json string which
13251     * specifies an array of node definition object representing the child nodes
13252     * to be loaded.
13253     */
13254     /**
13255     * @cfg {String} requestMethod either GET or POST
13256     * defaults to POST (due to BC)
13257     * to be loaded.
13258     */
13259     /**
13260     * @cfg {Object} baseParams (optional) An object containing properties which
13261     * specify HTTP parameters to be passed to each request for child nodes.
13262     */
13263     /**
13264     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13265     * created by this loader. If the attributes sent by the server have an attribute in this object,
13266     * they take priority.
13267     */
13268     /**
13269     * @cfg {Object} uiProviders (optional) An object containing properties which
13270     * 
13271     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13272     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13273     * <i>uiProvider</i> attribute of a returned child node is a string rather
13274     * than a reference to a TreeNodeUI implementation, this that string value
13275     * is used as a property name in the uiProviders object. You can define the provider named
13276     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13277     */
13278     uiProviders : {},
13279
13280     /**
13281     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13282     * child nodes before loading.
13283     */
13284     clearOnLoad : true,
13285
13286     /**
13287     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13288     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13289     * Grid query { data : [ .....] }
13290     */
13291     
13292     root : false,
13293      /**
13294     * @cfg {String} queryParam (optional) 
13295     * Name of the query as it will be passed on the querystring (defaults to 'node')
13296     * eg. the request will be ?node=[id]
13297     */
13298     
13299     
13300     queryParam: false,
13301     
13302     /**
13303      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13304      * This is called automatically when a node is expanded, but may be used to reload
13305      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13306      * @param {Roo.tree.TreeNode} node
13307      * @param {Function} callback
13308      */
13309     load : function(node, callback){
13310         if(this.clearOnLoad){
13311             while(node.firstChild){
13312                 node.removeChild(node.firstChild);
13313             }
13314         }
13315         if(node.attributes.children){ // preloaded json children
13316             var cs = node.attributes.children;
13317             for(var i = 0, len = cs.length; i < len; i++){
13318                 node.appendChild(this.createNode(cs[i]));
13319             }
13320             if(typeof callback == "function"){
13321                 callback();
13322             }
13323         }else if(this.dataUrl){
13324             this.requestData(node, callback);
13325         }
13326     },
13327
13328     getParams: function(node){
13329         var buf = [], bp = this.baseParams;
13330         for(var key in bp){
13331             if(typeof bp[key] != "function"){
13332                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13333             }
13334         }
13335         var n = this.queryParam === false ? 'node' : this.queryParam;
13336         buf.push(n + "=", encodeURIComponent(node.id));
13337         return buf.join("");
13338     },
13339
13340     requestData : function(node, callback){
13341         if(this.fireEvent("beforeload", this, node, callback) !== false){
13342             this.transId = Roo.Ajax.request({
13343                 method:this.requestMethod,
13344                 url: this.dataUrl||this.url,
13345                 success: this.handleResponse,
13346                 failure: this.handleFailure,
13347                 scope: this,
13348                 argument: {callback: callback, node: node},
13349                 params: this.getParams(node)
13350             });
13351         }else{
13352             // if the load is cancelled, make sure we notify
13353             // the node that we are done
13354             if(typeof callback == "function"){
13355                 callback();
13356             }
13357         }
13358     },
13359
13360     isLoading : function(){
13361         return this.transId ? true : false;
13362     },
13363
13364     abort : function(){
13365         if(this.isLoading()){
13366             Roo.Ajax.abort(this.transId);
13367         }
13368     },
13369
13370     // private
13371     createNode : function(attr)
13372     {
13373         // apply baseAttrs, nice idea Corey!
13374         if(this.baseAttrs){
13375             Roo.applyIf(attr, this.baseAttrs);
13376         }
13377         if(this.applyLoader !== false){
13378             attr.loader = this;
13379         }
13380         // uiProvider = depreciated..
13381         
13382         if(typeof(attr.uiProvider) == 'string'){
13383            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13384                 /**  eval:var:attr */ eval(attr.uiProvider);
13385         }
13386         if(typeof(this.uiProviders['default']) != 'undefined') {
13387             attr.uiProvider = this.uiProviders['default'];
13388         }
13389         
13390         this.fireEvent('create', this, attr);
13391         
13392         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13393         return(attr.leaf ?
13394                         new Roo.tree.TreeNode(attr) :
13395                         new Roo.tree.AsyncTreeNode(attr));
13396     },
13397
13398     processResponse : function(response, node, callback)
13399     {
13400         var json = response.responseText;
13401         try {
13402             
13403             var o = Roo.decode(json);
13404             
13405             if (this.root === false && typeof(o.success) != undefined) {
13406                 this.root = 'data'; // the default behaviour for list like data..
13407                 }
13408                 
13409             if (this.root !== false &&  !o.success) {
13410                 // it's a failure condition.
13411                 var a = response.argument;
13412                 this.fireEvent("loadexception", this, a.node, response);
13413                 Roo.log("Load failed - should have a handler really");
13414                 return;
13415             }
13416             
13417             
13418             
13419             if (this.root !== false) {
13420                  o = o[this.root];
13421             }
13422             
13423             for(var i = 0, len = o.length; i < len; i++){
13424                 var n = this.createNode(o[i]);
13425                 if(n){
13426                     node.appendChild(n);
13427                 }
13428             }
13429             if(typeof callback == "function"){
13430                 callback(this, node);
13431             }
13432         }catch(e){
13433             this.handleFailure(response);
13434         }
13435     },
13436
13437     handleResponse : function(response){
13438         this.transId = false;
13439         var a = response.argument;
13440         this.processResponse(response, a.node, a.callback);
13441         this.fireEvent("load", this, a.node, response);
13442     },
13443
13444     handleFailure : function(response)
13445     {
13446         // should handle failure better..
13447         this.transId = false;
13448         var a = response.argument;
13449         this.fireEvent("loadexception", this, a.node, response);
13450         if(typeof a.callback == "function"){
13451             a.callback(this, a.node);
13452         }
13453     }
13454 });/*
13455  * Based on:
13456  * Ext JS Library 1.1.1
13457  * Copyright(c) 2006-2007, Ext JS, LLC.
13458  *
13459  * Originally Released Under LGPL - original licence link has changed is not relivant.
13460  *
13461  * Fork - LGPL
13462  * <script type="text/javascript">
13463  */
13464
13465 /**
13466 * @class Roo.tree.TreeFilter
13467 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13468 * @param {TreePanel} tree
13469 * @param {Object} config (optional)
13470  */
13471 Roo.tree.TreeFilter = function(tree, config){
13472     this.tree = tree;
13473     this.filtered = {};
13474     Roo.apply(this, config);
13475 };
13476
13477 Roo.tree.TreeFilter.prototype = {
13478     clearBlank:false,
13479     reverse:false,
13480     autoClear:false,
13481     remove:false,
13482
13483      /**
13484      * Filter the data by a specific attribute.
13485      * @param {String/RegExp} value Either string that the attribute value
13486      * should start with or a RegExp to test against the attribute
13487      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13488      * @param {TreeNode} startNode (optional) The node to start the filter at.
13489      */
13490     filter : function(value, attr, startNode){
13491         attr = attr || "text";
13492         var f;
13493         if(typeof value == "string"){
13494             var vlen = value.length;
13495             // auto clear empty filter
13496             if(vlen == 0 && this.clearBlank){
13497                 this.clear();
13498                 return;
13499             }
13500             value = value.toLowerCase();
13501             f = function(n){
13502                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13503             };
13504         }else if(value.exec){ // regex?
13505             f = function(n){
13506                 return value.test(n.attributes[attr]);
13507             };
13508         }else{
13509             throw 'Illegal filter type, must be string or regex';
13510         }
13511         this.filterBy(f, null, startNode);
13512         },
13513
13514     /**
13515      * Filter by a function. The passed function will be called with each
13516      * node in the tree (or from the startNode). If the function returns true, the node is kept
13517      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13518      * @param {Function} fn The filter function
13519      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13520      */
13521     filterBy : function(fn, scope, startNode){
13522         startNode = startNode || this.tree.root;
13523         if(this.autoClear){
13524             this.clear();
13525         }
13526         var af = this.filtered, rv = this.reverse;
13527         var f = function(n){
13528             if(n == startNode){
13529                 return true;
13530             }
13531             if(af[n.id]){
13532                 return false;
13533             }
13534             var m = fn.call(scope || n, n);
13535             if(!m || rv){
13536                 af[n.id] = n;
13537                 n.ui.hide();
13538                 return false;
13539             }
13540             return true;
13541         };
13542         startNode.cascade(f);
13543         if(this.remove){
13544            for(var id in af){
13545                if(typeof id != "function"){
13546                    var n = af[id];
13547                    if(n && n.parentNode){
13548                        n.parentNode.removeChild(n);
13549                    }
13550                }
13551            }
13552         }
13553     },
13554
13555     /**
13556      * Clears the current filter. Note: with the "remove" option
13557      * set a filter cannot be cleared.
13558      */
13559     clear : function(){
13560         var t = this.tree;
13561         var af = this.filtered;
13562         for(var id in af){
13563             if(typeof id != "function"){
13564                 var n = af[id];
13565                 if(n){
13566                     n.ui.show();
13567                 }
13568             }
13569         }
13570         this.filtered = {};
13571     }
13572 };
13573 /*
13574  * Based on:
13575  * Ext JS Library 1.1.1
13576  * Copyright(c) 2006-2007, Ext JS, LLC.
13577  *
13578  * Originally Released Under LGPL - original licence link has changed is not relivant.
13579  *
13580  * Fork - LGPL
13581  * <script type="text/javascript">
13582  */
13583  
13584
13585 /**
13586  * @class Roo.tree.TreeSorter
13587  * Provides sorting of nodes in a TreePanel
13588  * 
13589  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13590  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13591  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13592  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13593  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13594  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13595  * @constructor
13596  * @param {TreePanel} tree
13597  * @param {Object} config
13598  */
13599 Roo.tree.TreeSorter = function(tree, config){
13600     Roo.apply(this, config);
13601     tree.on("beforechildrenrendered", this.doSort, this);
13602     tree.on("append", this.updateSort, this);
13603     tree.on("insert", this.updateSort, this);
13604     
13605     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13606     var p = this.property || "text";
13607     var sortType = this.sortType;
13608     var fs = this.folderSort;
13609     var cs = this.caseSensitive === true;
13610     var leafAttr = this.leafAttr || 'leaf';
13611
13612     this.sortFn = function(n1, n2){
13613         if(fs){
13614             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13615                 return 1;
13616             }
13617             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13618                 return -1;
13619             }
13620         }
13621         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13622         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13623         if(v1 < v2){
13624                         return dsc ? +1 : -1;
13625                 }else if(v1 > v2){
13626                         return dsc ? -1 : +1;
13627         }else{
13628                 return 0;
13629         }
13630     };
13631 };
13632
13633 Roo.tree.TreeSorter.prototype = {
13634     doSort : function(node){
13635         node.sort(this.sortFn);
13636     },
13637     
13638     compareNodes : function(n1, n2){
13639         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13640     },
13641     
13642     updateSort : function(tree, node){
13643         if(node.childrenRendered){
13644             this.doSort.defer(1, this, [node]);
13645         }
13646     }
13647 };/*
13648  * Based on:
13649  * Ext JS Library 1.1.1
13650  * Copyright(c) 2006-2007, Ext JS, LLC.
13651  *
13652  * Originally Released Under LGPL - original licence link has changed is not relivant.
13653  *
13654  * Fork - LGPL
13655  * <script type="text/javascript">
13656  */
13657
13658 if(Roo.dd.DropZone){
13659     
13660 Roo.tree.TreeDropZone = function(tree, config){
13661     this.allowParentInsert = false;
13662     this.allowContainerDrop = false;
13663     this.appendOnly = false;
13664     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13665     this.tree = tree;
13666     this.lastInsertClass = "x-tree-no-status";
13667     this.dragOverData = {};
13668 };
13669
13670 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13671     ddGroup : "TreeDD",
13672     scroll:  true,
13673     
13674     expandDelay : 1000,
13675     
13676     expandNode : function(node){
13677         if(node.hasChildNodes() && !node.isExpanded()){
13678             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13679         }
13680     },
13681     
13682     queueExpand : function(node){
13683         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13684     },
13685     
13686     cancelExpand : function(){
13687         if(this.expandProcId){
13688             clearTimeout(this.expandProcId);
13689             this.expandProcId = false;
13690         }
13691     },
13692     
13693     isValidDropPoint : function(n, pt, dd, e, data){
13694         if(!n || !data){ return false; }
13695         var targetNode = n.node;
13696         var dropNode = data.node;
13697         // default drop rules
13698         if(!(targetNode && targetNode.isTarget && pt)){
13699             return false;
13700         }
13701         if(pt == "append" && targetNode.allowChildren === false){
13702             return false;
13703         }
13704         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13705             return false;
13706         }
13707         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13708             return false;
13709         }
13710         // reuse the object
13711         var overEvent = this.dragOverData;
13712         overEvent.tree = this.tree;
13713         overEvent.target = targetNode;
13714         overEvent.data = data;
13715         overEvent.point = pt;
13716         overEvent.source = dd;
13717         overEvent.rawEvent = e;
13718         overEvent.dropNode = dropNode;
13719         overEvent.cancel = false;  
13720         var result = this.tree.fireEvent("nodedragover", overEvent);
13721         return overEvent.cancel === false && result !== false;
13722     },
13723     
13724     getDropPoint : function(e, n, dd)
13725     {
13726         var tn = n.node;
13727         if(tn.isRoot){
13728             return tn.allowChildren !== false ? "append" : false; // always append for root
13729         }
13730         var dragEl = n.ddel;
13731         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13732         var y = Roo.lib.Event.getPageY(e);
13733         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13734         
13735         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13736         var noAppend = tn.allowChildren === false;
13737         if(this.appendOnly || tn.parentNode.allowChildren === false){
13738             return noAppend ? false : "append";
13739         }
13740         var noBelow = false;
13741         if(!this.allowParentInsert){
13742             noBelow = tn.hasChildNodes() && tn.isExpanded();
13743         }
13744         var q = (b - t) / (noAppend ? 2 : 3);
13745         if(y >= t && y < (t + q)){
13746             return "above";
13747         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13748             return "below";
13749         }else{
13750             return "append";
13751         }
13752     },
13753     
13754     onNodeEnter : function(n, dd, e, data)
13755     {
13756         this.cancelExpand();
13757     },
13758     
13759     onNodeOver : function(n, dd, e, data)
13760     {
13761        
13762         var pt = this.getDropPoint(e, n, dd);
13763         var node = n.node;
13764         
13765         // auto node expand check
13766         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13767             this.queueExpand(node);
13768         }else if(pt != "append"){
13769             this.cancelExpand();
13770         }
13771         
13772         // set the insert point style on the target node
13773         var returnCls = this.dropNotAllowed;
13774         if(this.isValidDropPoint(n, pt, dd, e, data)){
13775            if(pt){
13776                var el = n.ddel;
13777                var cls;
13778                if(pt == "above"){
13779                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13780                    cls = "x-tree-drag-insert-above";
13781                }else if(pt == "below"){
13782                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13783                    cls = "x-tree-drag-insert-below";
13784                }else{
13785                    returnCls = "x-tree-drop-ok-append";
13786                    cls = "x-tree-drag-append";
13787                }
13788                if(this.lastInsertClass != cls){
13789                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13790                    this.lastInsertClass = cls;
13791                }
13792            }
13793        }
13794        return returnCls;
13795     },
13796     
13797     onNodeOut : function(n, dd, e, data){
13798         
13799         this.cancelExpand();
13800         this.removeDropIndicators(n);
13801     },
13802     
13803     onNodeDrop : function(n, dd, e, data){
13804         var point = this.getDropPoint(e, n, dd);
13805         var targetNode = n.node;
13806         targetNode.ui.startDrop();
13807         if(!this.isValidDropPoint(n, point, dd, e, data)){
13808             targetNode.ui.endDrop();
13809             return false;
13810         }
13811         // first try to find the drop node
13812         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13813         var dropEvent = {
13814             tree : this.tree,
13815             target: targetNode,
13816             data: data,
13817             point: point,
13818             source: dd,
13819             rawEvent: e,
13820             dropNode: dropNode,
13821             cancel: !dropNode   
13822         };
13823         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13824         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13825             targetNode.ui.endDrop();
13826             return false;
13827         }
13828         // allow target changing
13829         targetNode = dropEvent.target;
13830         if(point == "append" && !targetNode.isExpanded()){
13831             targetNode.expand(false, null, function(){
13832                 this.completeDrop(dropEvent);
13833             }.createDelegate(this));
13834         }else{
13835             this.completeDrop(dropEvent);
13836         }
13837         return true;
13838     },
13839     
13840     completeDrop : function(de){
13841         var ns = de.dropNode, p = de.point, t = de.target;
13842         if(!(ns instanceof Array)){
13843             ns = [ns];
13844         }
13845         var n;
13846         for(var i = 0, len = ns.length; i < len; i++){
13847             n = ns[i];
13848             if(p == "above"){
13849                 t.parentNode.insertBefore(n, t);
13850             }else if(p == "below"){
13851                 t.parentNode.insertBefore(n, t.nextSibling);
13852             }else{
13853                 t.appendChild(n);
13854             }
13855         }
13856         n.ui.focus();
13857         if(this.tree.hlDrop){
13858             n.ui.highlight();
13859         }
13860         t.ui.endDrop();
13861         this.tree.fireEvent("nodedrop", de);
13862     },
13863     
13864     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13865         if(this.tree.hlDrop){
13866             dropNode.ui.focus();
13867             dropNode.ui.highlight();
13868         }
13869         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13870     },
13871     
13872     getTree : function(){
13873         return this.tree;
13874     },
13875     
13876     removeDropIndicators : function(n){
13877         if(n && n.ddel){
13878             var el = n.ddel;
13879             Roo.fly(el).removeClass([
13880                     "x-tree-drag-insert-above",
13881                     "x-tree-drag-insert-below",
13882                     "x-tree-drag-append"]);
13883             this.lastInsertClass = "_noclass";
13884         }
13885     },
13886     
13887     beforeDragDrop : function(target, e, id){
13888         this.cancelExpand();
13889         return true;
13890     },
13891     
13892     afterRepair : function(data){
13893         if(data && Roo.enableFx){
13894             data.node.ui.highlight();
13895         }
13896         this.hideProxy();
13897     } 
13898     
13899 });
13900
13901 }
13902 /*
13903  * Based on:
13904  * Ext JS Library 1.1.1
13905  * Copyright(c) 2006-2007, Ext JS, LLC.
13906  *
13907  * Originally Released Under LGPL - original licence link has changed is not relivant.
13908  *
13909  * Fork - LGPL
13910  * <script type="text/javascript">
13911  */
13912  
13913
13914 if(Roo.dd.DragZone){
13915 Roo.tree.TreeDragZone = function(tree, config){
13916     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13917     this.tree = tree;
13918 };
13919
13920 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13921     ddGroup : "TreeDD",
13922    
13923     onBeforeDrag : function(data, e){
13924         var n = data.node;
13925         return n && n.draggable && !n.disabled;
13926     },
13927      
13928     
13929     onInitDrag : function(e){
13930         var data = this.dragData;
13931         this.tree.getSelectionModel().select(data.node);
13932         this.proxy.update("");
13933         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13934         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13935     },
13936     
13937     getRepairXY : function(e, data){
13938         return data.node.ui.getDDRepairXY();
13939     },
13940     
13941     onEndDrag : function(data, e){
13942         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13943         
13944         
13945     },
13946     
13947     onValidDrop : function(dd, e, id){
13948         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13949         this.hideProxy();
13950     },
13951     
13952     beforeInvalidDrop : function(e, id){
13953         // this scrolls the original position back into view
13954         var sm = this.tree.getSelectionModel();
13955         sm.clearSelections();
13956         sm.select(this.dragData.node);
13957     }
13958 });
13959 }/*
13960  * Based on:
13961  * Ext JS Library 1.1.1
13962  * Copyright(c) 2006-2007, Ext JS, LLC.
13963  *
13964  * Originally Released Under LGPL - original licence link has changed is not relivant.
13965  *
13966  * Fork - LGPL
13967  * <script type="text/javascript">
13968  */
13969 /**
13970  * @class Roo.tree.TreeEditor
13971  * @extends Roo.Editor
13972  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13973  * as the editor field.
13974  * @constructor
13975  * @param {Object} config (used to be the tree panel.)
13976  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13977  * 
13978  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13979  * @cfg {Roo.form.TextField|Object} field The field configuration
13980  *
13981  * 
13982  */
13983 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13984     var tree = config;
13985     var field;
13986     if (oldconfig) { // old style..
13987         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13988     } else {
13989         // new style..
13990         tree = config.tree;
13991         config.field = config.field  || {};
13992         config.field.xtype = 'TextField';
13993         field = Roo.factory(config.field, Roo.form);
13994     }
13995     config = config || {};
13996     
13997     
13998     this.addEvents({
13999         /**
14000          * @event beforenodeedit
14001          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14002          * false from the handler of this event.
14003          * @param {Editor} this
14004          * @param {Roo.tree.Node} node 
14005          */
14006         "beforenodeedit" : true
14007     });
14008     
14009     //Roo.log(config);
14010     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14011
14012     this.tree = tree;
14013
14014     tree.on('beforeclick', this.beforeNodeClick, this);
14015     tree.getTreeEl().on('mousedown', this.hide, this);
14016     this.on('complete', this.updateNode, this);
14017     this.on('beforestartedit', this.fitToTree, this);
14018     this.on('startedit', this.bindScroll, this, {delay:10});
14019     this.on('specialkey', this.onSpecialKey, this);
14020 };
14021
14022 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14023     /**
14024      * @cfg {String} alignment
14025      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14026      */
14027     alignment: "l-l",
14028     // inherit
14029     autoSize: false,
14030     /**
14031      * @cfg {Boolean} hideEl
14032      * True to hide the bound element while the editor is displayed (defaults to false)
14033      */
14034     hideEl : false,
14035     /**
14036      * @cfg {String} cls
14037      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14038      */
14039     cls: "x-small-editor x-tree-editor",
14040     /**
14041      * @cfg {Boolean} shim
14042      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14043      */
14044     shim:false,
14045     // inherit
14046     shadow:"frame",
14047     /**
14048      * @cfg {Number} maxWidth
14049      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14050      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14051      * scroll and client offsets into account prior to each edit.
14052      */
14053     maxWidth: 250,
14054
14055     editDelay : 350,
14056
14057     // private
14058     fitToTree : function(ed, el){
14059         var td = this.tree.getTreeEl().dom, nd = el.dom;
14060         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14061             td.scrollLeft = nd.offsetLeft;
14062         }
14063         var w = Math.min(
14064                 this.maxWidth,
14065                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14066         this.setSize(w, '');
14067         
14068         return this.fireEvent('beforenodeedit', this, this.editNode);
14069         
14070     },
14071
14072     // private
14073     triggerEdit : function(node){
14074         this.completeEdit();
14075         this.editNode = node;
14076         this.startEdit(node.ui.textNode, node.text);
14077     },
14078
14079     // private
14080     bindScroll : function(){
14081         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14082     },
14083
14084     // private
14085     beforeNodeClick : function(node, e){
14086         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14087         this.lastClick = new Date();
14088         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14089             e.stopEvent();
14090             this.triggerEdit(node);
14091             return false;
14092         }
14093         return true;
14094     },
14095
14096     // private
14097     updateNode : function(ed, value){
14098         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14099         this.editNode.setText(value);
14100     },
14101
14102     // private
14103     onHide : function(){
14104         Roo.tree.TreeEditor.superclass.onHide.call(this);
14105         if(this.editNode){
14106             this.editNode.ui.focus();
14107         }
14108     },
14109
14110     // private
14111     onSpecialKey : function(field, e){
14112         var k = e.getKey();
14113         if(k == e.ESC){
14114             e.stopEvent();
14115             this.cancelEdit();
14116         }else if(k == e.ENTER && !e.hasModifier()){
14117             e.stopEvent();
14118             this.completeEdit();
14119         }
14120     }
14121 });//<Script type="text/javascript">
14122 /*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132  
14133 /**
14134  * Not documented??? - probably should be...
14135  */
14136
14137 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14138     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14139     
14140     renderElements : function(n, a, targetNode, bulkRender){
14141         //consel.log("renderElements?");
14142         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14143
14144         var t = n.getOwnerTree();
14145         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14146         
14147         var cols = t.columns;
14148         var bw = t.borderWidth;
14149         var c = cols[0];
14150         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14151          var cb = typeof a.checked == "boolean";
14152         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14153         var colcls = 'x-t-' + tid + '-c0';
14154         var buf = [
14155             '<li class="x-tree-node">',
14156             
14157                 
14158                 '<div class="x-tree-node-el ', a.cls,'">',
14159                     // extran...
14160                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14161                 
14162                 
14163                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14164                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14165                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14166                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14167                            (a.iconCls ? ' '+a.iconCls : ''),
14168                            '" unselectable="on" />',
14169                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14170                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14171                              
14172                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14173                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14174                             '<span unselectable="on" qtip="' + tx + '">',
14175                              tx,
14176                              '</span></a>' ,
14177                     '</div>',
14178                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14179                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14180                  ];
14181         for(var i = 1, len = cols.length; i < len; i++){
14182             c = cols[i];
14183             colcls = 'x-t-' + tid + '-c' +i;
14184             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14185             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14186                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14187                       "</div>");
14188          }
14189          
14190          buf.push(
14191             '</a>',
14192             '<div class="x-clear"></div></div>',
14193             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14194             "</li>");
14195         
14196         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14197             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14198                                 n.nextSibling.ui.getEl(), buf.join(""));
14199         }else{
14200             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14201         }
14202         var el = this.wrap.firstChild;
14203         this.elRow = el;
14204         this.elNode = el.firstChild;
14205         this.ranchor = el.childNodes[1];
14206         this.ctNode = this.wrap.childNodes[1];
14207         var cs = el.firstChild.childNodes;
14208         this.indentNode = cs[0];
14209         this.ecNode = cs[1];
14210         this.iconNode = cs[2];
14211         var index = 3;
14212         if(cb){
14213             this.checkbox = cs[3];
14214             index++;
14215         }
14216         this.anchor = cs[index];
14217         
14218         this.textNode = cs[index].firstChild;
14219         
14220         //el.on("click", this.onClick, this);
14221         //el.on("dblclick", this.onDblClick, this);
14222         
14223         
14224        // console.log(this);
14225     },
14226     initEvents : function(){
14227         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14228         
14229             
14230         var a = this.ranchor;
14231
14232         var el = Roo.get(a);
14233
14234         if(Roo.isOpera){ // opera render bug ignores the CSS
14235             el.setStyle("text-decoration", "none");
14236         }
14237
14238         el.on("click", this.onClick, this);
14239         el.on("dblclick", this.onDblClick, this);
14240         el.on("contextmenu", this.onContextMenu, this);
14241         
14242     },
14243     
14244     /*onSelectedChange : function(state){
14245         if(state){
14246             this.focus();
14247             this.addClass("x-tree-selected");
14248         }else{
14249             //this.blur();
14250             this.removeClass("x-tree-selected");
14251         }
14252     },*/
14253     addClass : function(cls){
14254         if(this.elRow){
14255             Roo.fly(this.elRow).addClass(cls);
14256         }
14257         
14258     },
14259     
14260     
14261     removeClass : function(cls){
14262         if(this.elRow){
14263             Roo.fly(this.elRow).removeClass(cls);
14264         }
14265     }
14266
14267     
14268     
14269 });//<Script type="text/javascript">
14270
14271 /*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281  
14282
14283 /**
14284  * @class Roo.tree.ColumnTree
14285  * @extends Roo.data.TreePanel
14286  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14287  * @cfg {int} borderWidth  compined right/left border allowance
14288  * @constructor
14289  * @param {String/HTMLElement/Element} el The container element
14290  * @param {Object} config
14291  */
14292 Roo.tree.ColumnTree =  function(el, config)
14293 {
14294    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14295    this.addEvents({
14296         /**
14297         * @event resize
14298         * Fire this event on a container when it resizes
14299         * @param {int} w Width
14300         * @param {int} h Height
14301         */
14302        "resize" : true
14303     });
14304     this.on('resize', this.onResize, this);
14305 };
14306
14307 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14308     //lines:false,
14309     
14310     
14311     borderWidth: Roo.isBorderBox ? 0 : 2, 
14312     headEls : false,
14313     
14314     render : function(){
14315         // add the header.....
14316        
14317         Roo.tree.ColumnTree.superclass.render.apply(this);
14318         
14319         this.el.addClass('x-column-tree');
14320         
14321         this.headers = this.el.createChild(
14322             {cls:'x-tree-headers'},this.innerCt.dom);
14323    
14324         var cols = this.columns, c;
14325         var totalWidth = 0;
14326         this.headEls = [];
14327         var  len = cols.length;
14328         for(var i = 0; i < len; i++){
14329              c = cols[i];
14330              totalWidth += c.width;
14331             this.headEls.push(this.headers.createChild({
14332                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14333                  cn: {
14334                      cls:'x-tree-hd-text',
14335                      html: c.header
14336                  },
14337                  style:'width:'+(c.width-this.borderWidth)+'px;'
14338              }));
14339         }
14340         this.headers.createChild({cls:'x-clear'});
14341         // prevent floats from wrapping when clipped
14342         this.headers.setWidth(totalWidth);
14343         //this.innerCt.setWidth(totalWidth);
14344         this.innerCt.setStyle({ overflow: 'auto' });
14345         this.onResize(this.width, this.height);
14346              
14347         
14348     },
14349     onResize : function(w,h)
14350     {
14351         this.height = h;
14352         this.width = w;
14353         // resize cols..
14354         this.innerCt.setWidth(this.width);
14355         this.innerCt.setHeight(this.height-20);
14356         
14357         // headers...
14358         var cols = this.columns, c;
14359         var totalWidth = 0;
14360         var expEl = false;
14361         var len = cols.length;
14362         for(var i = 0; i < len; i++){
14363             c = cols[i];
14364             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14365                 // it's the expander..
14366                 expEl  = this.headEls[i];
14367                 continue;
14368             }
14369             totalWidth += c.width;
14370             
14371         }
14372         if (expEl) {
14373             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14374         }
14375         this.headers.setWidth(w-20);
14376
14377         
14378         
14379         
14380     }
14381 });
14382 /*
14383  * Based on:
14384  * Ext JS Library 1.1.1
14385  * Copyright(c) 2006-2007, Ext JS, LLC.
14386  *
14387  * Originally Released Under LGPL - original licence link has changed is not relivant.
14388  *
14389  * Fork - LGPL
14390  * <script type="text/javascript">
14391  */
14392  
14393 /**
14394  * @class Roo.menu.Menu
14395  * @extends Roo.util.Observable
14396  * @children Roo.menu.BaseItem
14397  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14398  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14399  * @constructor
14400  * Creates a new Menu
14401  * @param {Object} config Configuration options
14402  */
14403 Roo.menu.Menu = function(config){
14404     
14405     Roo.menu.Menu.superclass.constructor.call(this, config);
14406     
14407     this.id = this.id || Roo.id();
14408     this.addEvents({
14409         /**
14410          * @event beforeshow
14411          * Fires before this menu is displayed
14412          * @param {Roo.menu.Menu} this
14413          */
14414         beforeshow : true,
14415         /**
14416          * @event beforehide
14417          * Fires before this menu is hidden
14418          * @param {Roo.menu.Menu} this
14419          */
14420         beforehide : true,
14421         /**
14422          * @event show
14423          * Fires after this menu is displayed
14424          * @param {Roo.menu.Menu} this
14425          */
14426         show : true,
14427         /**
14428          * @event hide
14429          * Fires after this menu is hidden
14430          * @param {Roo.menu.Menu} this
14431          */
14432         hide : true,
14433         /**
14434          * @event click
14435          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14436          * @param {Roo.menu.Menu} this
14437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14438          * @param {Roo.EventObject} e
14439          */
14440         click : true,
14441         /**
14442          * @event mouseover
14443          * Fires when the mouse is hovering over this menu
14444          * @param {Roo.menu.Menu} this
14445          * @param {Roo.EventObject} e
14446          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14447          */
14448         mouseover : true,
14449         /**
14450          * @event mouseout
14451          * Fires when the mouse exits this menu
14452          * @param {Roo.menu.Menu} this
14453          * @param {Roo.EventObject} e
14454          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14455          */
14456         mouseout : true,
14457         /**
14458          * @event itemclick
14459          * Fires when a menu item contained in this menu is clicked
14460          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14461          * @param {Roo.EventObject} e
14462          */
14463         itemclick: true
14464     });
14465     if (this.registerMenu) {
14466         Roo.menu.MenuMgr.register(this);
14467     }
14468     
14469     var mis = this.items;
14470     this.items = new Roo.util.MixedCollection();
14471     if(mis){
14472         this.add.apply(this, mis);
14473     }
14474 };
14475
14476 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14477     /**
14478      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14479      */
14480     minWidth : 120,
14481     /**
14482      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14483      * for bottom-right shadow (defaults to "sides")
14484      */
14485     shadow : "sides",
14486     /**
14487      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14488      * this menu (defaults to "tl-tr?")
14489      */
14490     subMenuAlign : "tl-tr?",
14491     /**
14492      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14493      * relative to its element of origin (defaults to "tl-bl?")
14494      */
14495     defaultAlign : "tl-bl?",
14496     /**
14497      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14498      */
14499     allowOtherMenus : false,
14500     /**
14501      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14502      */
14503     registerMenu : true,
14504
14505     hidden:true,
14506
14507     // private
14508     render : function(){
14509         if(this.el){
14510             return;
14511         }
14512         var el = this.el = new Roo.Layer({
14513             cls: "x-menu",
14514             shadow:this.shadow,
14515             constrain: false,
14516             parentEl: this.parentEl || document.body,
14517             zindex:15000
14518         });
14519
14520         this.keyNav = new Roo.menu.MenuNav(this);
14521
14522         if(this.plain){
14523             el.addClass("x-menu-plain");
14524         }
14525         if(this.cls){
14526             el.addClass(this.cls);
14527         }
14528         // generic focus element
14529         this.focusEl = el.createChild({
14530             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14531         });
14532         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14533         //disabling touch- as it's causing issues ..
14534         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14535         ul.on('click'   , this.onClick, this);
14536         
14537         
14538         ul.on("mouseover", this.onMouseOver, this);
14539         ul.on("mouseout", this.onMouseOut, this);
14540         this.items.each(function(item){
14541             if (item.hidden) {
14542                 return;
14543             }
14544             
14545             var li = document.createElement("li");
14546             li.className = "x-menu-list-item";
14547             ul.dom.appendChild(li);
14548             item.render(li, this);
14549         }, this);
14550         this.ul = ul;
14551         this.autoWidth();
14552     },
14553
14554     // private
14555     autoWidth : function(){
14556         var el = this.el, ul = this.ul;
14557         if(!el){
14558             return;
14559         }
14560         var w = this.width;
14561         if(w){
14562             el.setWidth(w);
14563         }else if(Roo.isIE){
14564             el.setWidth(this.minWidth);
14565             var t = el.dom.offsetWidth; // force recalc
14566             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14567         }
14568     },
14569
14570     // private
14571     delayAutoWidth : function(){
14572         if(this.rendered){
14573             if(!this.awTask){
14574                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14575             }
14576             this.awTask.delay(20);
14577         }
14578     },
14579
14580     // private
14581     findTargetItem : function(e){
14582         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14583         if(t && t.menuItemId){
14584             return this.items.get(t.menuItemId);
14585         }
14586     },
14587
14588     // private
14589     onClick : function(e){
14590         Roo.log("menu.onClick");
14591         var t = this.findTargetItem(e);
14592         if(!t){
14593             return;
14594         }
14595         Roo.log(e);
14596         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14597             if(t == this.activeItem && t.shouldDeactivate(e)){
14598                 this.activeItem.deactivate();
14599                 delete this.activeItem;
14600                 return;
14601             }
14602             if(t.canActivate){
14603                 this.setActiveItem(t, true);
14604             }
14605             return;
14606             
14607             
14608         }
14609         
14610         t.onClick(e);
14611         this.fireEvent("click", this, t, e);
14612     },
14613
14614     // private
14615     setActiveItem : function(item, autoExpand){
14616         if(item != this.activeItem){
14617             if(this.activeItem){
14618                 this.activeItem.deactivate();
14619             }
14620             this.activeItem = item;
14621             item.activate(autoExpand);
14622         }else if(autoExpand){
14623             item.expandMenu();
14624         }
14625     },
14626
14627     // private
14628     tryActivate : function(start, step){
14629         var items = this.items;
14630         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14631             var item = items.get(i);
14632             if(!item.disabled && item.canActivate){
14633                 this.setActiveItem(item, false);
14634                 return item;
14635             }
14636         }
14637         return false;
14638     },
14639
14640     // private
14641     onMouseOver : function(e){
14642         var t;
14643         if(t = this.findTargetItem(e)){
14644             if(t.canActivate && !t.disabled){
14645                 this.setActiveItem(t, true);
14646             }
14647         }
14648         this.fireEvent("mouseover", this, e, t);
14649     },
14650
14651     // private
14652     onMouseOut : function(e){
14653         var t;
14654         if(t = this.findTargetItem(e)){
14655             if(t == this.activeItem && t.shouldDeactivate(e)){
14656                 this.activeItem.deactivate();
14657                 delete this.activeItem;
14658             }
14659         }
14660         this.fireEvent("mouseout", this, e, t);
14661     },
14662
14663     /**
14664      * Read-only.  Returns true if the menu is currently displayed, else false.
14665      * @type Boolean
14666      */
14667     isVisible : function(){
14668         return this.el && !this.hidden;
14669     },
14670
14671     /**
14672      * Displays this menu relative to another element
14673      * @param {String/HTMLElement/Roo.Element} element The element to align to
14674      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14675      * the element (defaults to this.defaultAlign)
14676      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14677      */
14678     show : function(el, pos, parentMenu){
14679         this.parentMenu = parentMenu;
14680         if(!this.el){
14681             this.render();
14682         }
14683         this.fireEvent("beforeshow", this);
14684         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14685     },
14686
14687     /**
14688      * Displays this menu at a specific xy position
14689      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14690      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14691      */
14692     showAt : function(xy, parentMenu, /* private: */_e){
14693         this.parentMenu = parentMenu;
14694         if(!this.el){
14695             this.render();
14696         }
14697         if(_e !== false){
14698             this.fireEvent("beforeshow", this);
14699             xy = this.el.adjustForConstraints(xy);
14700         }
14701         this.el.setXY(xy);
14702         this.el.show();
14703         this.hidden = false;
14704         this.focus();
14705         this.fireEvent("show", this);
14706     },
14707
14708     focus : function(){
14709         if(!this.hidden){
14710             this.doFocus.defer(50, this);
14711         }
14712     },
14713
14714     doFocus : function(){
14715         if(!this.hidden){
14716             this.focusEl.focus();
14717         }
14718     },
14719
14720     /**
14721      * Hides this menu and optionally all parent menus
14722      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14723      */
14724     hide : function(deep){
14725         if(this.el && this.isVisible()){
14726             this.fireEvent("beforehide", this);
14727             if(this.activeItem){
14728                 this.activeItem.deactivate();
14729                 this.activeItem = null;
14730             }
14731             this.el.hide();
14732             this.hidden = true;
14733             this.fireEvent("hide", this);
14734         }
14735         if(deep === true && this.parentMenu){
14736             this.parentMenu.hide(true);
14737         }
14738     },
14739
14740     /**
14741      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14742      * Any of the following are valid:
14743      * <ul>
14744      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14745      * <li>An HTMLElement object which will be converted to a menu item</li>
14746      * <li>A menu item config object that will be created as a new menu item</li>
14747      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14748      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14749      * </ul>
14750      * Usage:
14751      * <pre><code>
14752 // Create the menu
14753 var menu = new Roo.menu.Menu();
14754
14755 // Create a menu item to add by reference
14756 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14757
14758 // Add a bunch of items at once using different methods.
14759 // Only the last item added will be returned.
14760 var item = menu.add(
14761     menuItem,                // add existing item by ref
14762     'Dynamic Item',          // new TextItem
14763     '-',                     // new separator
14764     { text: 'Config Item' }  // new item by config
14765 );
14766 </code></pre>
14767      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14768      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14769      */
14770     add : function(){
14771         var a = arguments, l = a.length, item;
14772         for(var i = 0; i < l; i++){
14773             var el = a[i];
14774             if ((typeof(el) == "object") && el.xtype && el.xns) {
14775                 el = Roo.factory(el, Roo.menu);
14776             }
14777             
14778             if(el.render){ // some kind of Item
14779                 item = this.addItem(el);
14780             }else if(typeof el == "string"){ // string
14781                 if(el == "separator" || el == "-"){
14782                     item = this.addSeparator();
14783                 }else{
14784                     item = this.addText(el);
14785                 }
14786             }else if(el.tagName || el.el){ // element
14787                 item = this.addElement(el);
14788             }else if(typeof el == "object"){ // must be menu item config?
14789                 item = this.addMenuItem(el);
14790             }
14791         }
14792         return item;
14793     },
14794
14795     /**
14796      * Returns this menu's underlying {@link Roo.Element} object
14797      * @return {Roo.Element} The element
14798      */
14799     getEl : function(){
14800         if(!this.el){
14801             this.render();
14802         }
14803         return this.el;
14804     },
14805
14806     /**
14807      * Adds a separator bar to the menu
14808      * @return {Roo.menu.Item} The menu item that was added
14809      */
14810     addSeparator : function(){
14811         return this.addItem(new Roo.menu.Separator());
14812     },
14813
14814     /**
14815      * Adds an {@link Roo.Element} object to the menu
14816      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14817      * @return {Roo.menu.Item} The menu item that was added
14818      */
14819     addElement : function(el){
14820         return this.addItem(new Roo.menu.BaseItem(el));
14821     },
14822
14823     /**
14824      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14825      * @param {Roo.menu.Item} item The menu item to add
14826      * @return {Roo.menu.Item} The menu item that was added
14827      */
14828     addItem : function(item){
14829         this.items.add(item);
14830         if(this.ul){
14831             var li = document.createElement("li");
14832             li.className = "x-menu-list-item";
14833             this.ul.dom.appendChild(li);
14834             item.render(li, this);
14835             this.delayAutoWidth();
14836         }
14837         return item;
14838     },
14839
14840     /**
14841      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14842      * @param {Object} config A MenuItem config object
14843      * @return {Roo.menu.Item} The menu item that was added
14844      */
14845     addMenuItem : function(config){
14846         if(!(config instanceof Roo.menu.Item)){
14847             if(typeof config.checked == "boolean"){ // must be check menu item config?
14848                 config = new Roo.menu.CheckItem(config);
14849             }else{
14850                 config = new Roo.menu.Item(config);
14851             }
14852         }
14853         return this.addItem(config);
14854     },
14855
14856     /**
14857      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14858      * @param {String} text The text to display in the menu item
14859      * @return {Roo.menu.Item} The menu item that was added
14860      */
14861     addText : function(text){
14862         return this.addItem(new Roo.menu.TextItem({ text : text }));
14863     },
14864
14865     /**
14866      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14867      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14868      * @param {Roo.menu.Item} item The menu item to add
14869      * @return {Roo.menu.Item} The menu item that was added
14870      */
14871     insert : function(index, item){
14872         this.items.insert(index, item);
14873         if(this.ul){
14874             var li = document.createElement("li");
14875             li.className = "x-menu-list-item";
14876             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14877             item.render(li, this);
14878             this.delayAutoWidth();
14879         }
14880         return item;
14881     },
14882
14883     /**
14884      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14885      * @param {Roo.menu.Item} item The menu item to remove
14886      */
14887     remove : function(item){
14888         this.items.removeKey(item.id);
14889         item.destroy();
14890     },
14891
14892     /**
14893      * Removes and destroys all items in the menu
14894      */
14895     removeAll : function(){
14896         var f;
14897         while(f = this.items.first()){
14898             this.remove(f);
14899         }
14900     }
14901 });
14902
14903 // MenuNav is a private utility class used internally by the Menu
14904 Roo.menu.MenuNav = function(menu){
14905     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14906     this.scope = this.menu = menu;
14907 };
14908
14909 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14910     doRelay : function(e, h){
14911         var k = e.getKey();
14912         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14913             this.menu.tryActivate(0, 1);
14914             return false;
14915         }
14916         return h.call(this.scope || this, e, this.menu);
14917     },
14918
14919     up : function(e, m){
14920         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14921             m.tryActivate(m.items.length-1, -1);
14922         }
14923     },
14924
14925     down : function(e, m){
14926         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14927             m.tryActivate(0, 1);
14928         }
14929     },
14930
14931     right : function(e, m){
14932         if(m.activeItem){
14933             m.activeItem.expandMenu(true);
14934         }
14935     },
14936
14937     left : function(e, m){
14938         m.hide();
14939         if(m.parentMenu && m.parentMenu.activeItem){
14940             m.parentMenu.activeItem.activate();
14941         }
14942     },
14943
14944     enter : function(e, m){
14945         if(m.activeItem){
14946             e.stopPropagation();
14947             m.activeItem.onClick(e);
14948             m.fireEvent("click", this, m.activeItem);
14949             return true;
14950         }
14951     }
14952 });/*
14953  * Based on:
14954  * Ext JS Library 1.1.1
14955  * Copyright(c) 2006-2007, Ext JS, LLC.
14956  *
14957  * Originally Released Under LGPL - original licence link has changed is not relivant.
14958  *
14959  * Fork - LGPL
14960  * <script type="text/javascript">
14961  */
14962  
14963 /**
14964  * @class Roo.menu.MenuMgr
14965  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14966  * @singleton
14967  */
14968 Roo.menu.MenuMgr = function(){
14969    var menus, active, groups = {}, attached = false, lastShow = new Date();
14970
14971    // private - called when first menu is created
14972    function init(){
14973        menus = {};
14974        active = new Roo.util.MixedCollection();
14975        Roo.get(document).addKeyListener(27, function(){
14976            if(active.length > 0){
14977                hideAll();
14978            }
14979        });
14980    }
14981
14982    // private
14983    function hideAll(){
14984        if(active && active.length > 0){
14985            var c = active.clone();
14986            c.each(function(m){
14987                m.hide();
14988            });
14989        }
14990    }
14991
14992    // private
14993    function onHide(m){
14994        active.remove(m);
14995        if(active.length < 1){
14996            Roo.get(document).un("mousedown", onMouseDown);
14997            attached = false;
14998        }
14999    }
15000
15001    // private
15002    function onShow(m){
15003        var last = active.last();
15004        lastShow = new Date();
15005        active.add(m);
15006        if(!attached){
15007            Roo.get(document).on("mousedown", onMouseDown);
15008            attached = true;
15009        }
15010        if(m.parentMenu){
15011           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15012           m.parentMenu.activeChild = m;
15013        }else if(last && last.isVisible()){
15014           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15015        }
15016    }
15017
15018    // private
15019    function onBeforeHide(m){
15020        if(m.activeChild){
15021            m.activeChild.hide();
15022        }
15023        if(m.autoHideTimer){
15024            clearTimeout(m.autoHideTimer);
15025            delete m.autoHideTimer;
15026        }
15027    }
15028
15029    // private
15030    function onBeforeShow(m){
15031        var pm = m.parentMenu;
15032        if(!pm && !m.allowOtherMenus){
15033            hideAll();
15034        }else if(pm && pm.activeChild && active != m){
15035            pm.activeChild.hide();
15036        }
15037    }
15038
15039    // private
15040    function onMouseDown(e){
15041        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15042            hideAll();
15043        }
15044    }
15045
15046    // private
15047    function onBeforeCheck(mi, state){
15048        if(state){
15049            var g = groups[mi.group];
15050            for(var i = 0, l = g.length; i < l; i++){
15051                if(g[i] != mi){
15052                    g[i].setChecked(false);
15053                }
15054            }
15055        }
15056    }
15057
15058    return {
15059
15060        /**
15061         * Hides all menus that are currently visible
15062         */
15063        hideAll : function(){
15064             hideAll();  
15065        },
15066
15067        // private
15068        register : function(menu){
15069            if(!menus){
15070                init();
15071            }
15072            menus[menu.id] = menu;
15073            menu.on("beforehide", onBeforeHide);
15074            menu.on("hide", onHide);
15075            menu.on("beforeshow", onBeforeShow);
15076            menu.on("show", onShow);
15077            var g = menu.group;
15078            if(g && menu.events["checkchange"]){
15079                if(!groups[g]){
15080                    groups[g] = [];
15081                }
15082                groups[g].push(menu);
15083                menu.on("checkchange", onCheck);
15084            }
15085        },
15086
15087         /**
15088          * Returns a {@link Roo.menu.Menu} object
15089          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15090          * be used to generate and return a new Menu instance.
15091          */
15092        get : function(menu){
15093            if(typeof menu == "string"){ // menu id
15094                return menus[menu];
15095            }else if(menu.events){  // menu instance
15096                return menu;
15097            }else if(typeof menu.length == 'number'){ // array of menu items?
15098                return new Roo.menu.Menu({items:menu});
15099            }else{ // otherwise, must be a config
15100                return new Roo.menu.Menu(menu);
15101            }
15102        },
15103
15104        // private
15105        unregister : function(menu){
15106            delete menus[menu.id];
15107            menu.un("beforehide", onBeforeHide);
15108            menu.un("hide", onHide);
15109            menu.un("beforeshow", onBeforeShow);
15110            menu.un("show", onShow);
15111            var g = menu.group;
15112            if(g && menu.events["checkchange"]){
15113                groups[g].remove(menu);
15114                menu.un("checkchange", onCheck);
15115            }
15116        },
15117
15118        // private
15119        registerCheckable : function(menuItem){
15120            var g = menuItem.group;
15121            if(g){
15122                if(!groups[g]){
15123                    groups[g] = [];
15124                }
15125                groups[g].push(menuItem);
15126                menuItem.on("beforecheckchange", onBeforeCheck);
15127            }
15128        },
15129
15130        // private
15131        unregisterCheckable : function(menuItem){
15132            var g = menuItem.group;
15133            if(g){
15134                groups[g].remove(menuItem);
15135                menuItem.un("beforecheckchange", onBeforeCheck);
15136            }
15137        }
15138    };
15139 }();/*
15140  * Based on:
15141  * Ext JS Library 1.1.1
15142  * Copyright(c) 2006-2007, Ext JS, LLC.
15143  *
15144  * Originally Released Under LGPL - original licence link has changed is not relivant.
15145  *
15146  * Fork - LGPL
15147  * <script type="text/javascript">
15148  */
15149  
15150
15151 /**
15152  * @class Roo.menu.BaseItem
15153  * @extends Roo.Component
15154  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15155  * management and base configuration options shared by all menu components.
15156  * @constructor
15157  * Creates a new BaseItem
15158  * @param {Object} config Configuration options
15159  */
15160 Roo.menu.BaseItem = function(config){
15161     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15162
15163     this.addEvents({
15164         /**
15165          * @event click
15166          * Fires when this item is clicked
15167          * @param {Roo.menu.BaseItem} this
15168          * @param {Roo.EventObject} e
15169          */
15170         click: true,
15171         /**
15172          * @event activate
15173          * Fires when this item is activated
15174          * @param {Roo.menu.BaseItem} this
15175          */
15176         activate : true,
15177         /**
15178          * @event deactivate
15179          * Fires when this item is deactivated
15180          * @param {Roo.menu.BaseItem} this
15181          */
15182         deactivate : true
15183     });
15184
15185     if(this.handler){
15186         this.on("click", this.handler, this.scope, true);
15187     }
15188 };
15189
15190 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15191     /**
15192      * @cfg {Function} handler
15193      * A function that will handle the click event of this menu item (defaults to undefined)
15194      */
15195     /**
15196      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15197      */
15198     canActivate : false,
15199     
15200      /**
15201      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15202      */
15203     hidden: false,
15204     
15205     /**
15206      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15207      */
15208     activeClass : "x-menu-item-active",
15209     /**
15210      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15211      */
15212     hideOnClick : true,
15213     /**
15214      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15215      */
15216     hideDelay : 100,
15217
15218     // private
15219     ctype: "Roo.menu.BaseItem",
15220
15221     // private
15222     actionMode : "container",
15223
15224     // private
15225     render : function(container, parentMenu){
15226         this.parentMenu = parentMenu;
15227         Roo.menu.BaseItem.superclass.render.call(this, container);
15228         this.container.menuItemId = this.id;
15229     },
15230
15231     // private
15232     onRender : function(container, position){
15233         this.el = Roo.get(this.el);
15234         container.dom.appendChild(this.el.dom);
15235     },
15236
15237     // private
15238     onClick : function(e){
15239         if(!this.disabled && this.fireEvent("click", this, e) !== false
15240                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15241             this.handleClick(e);
15242         }else{
15243             e.stopEvent();
15244         }
15245     },
15246
15247     // private
15248     activate : function(){
15249         if(this.disabled){
15250             return false;
15251         }
15252         var li = this.container;
15253         li.addClass(this.activeClass);
15254         this.region = li.getRegion().adjust(2, 2, -2, -2);
15255         this.fireEvent("activate", this);
15256         return true;
15257     },
15258
15259     // private
15260     deactivate : function(){
15261         this.container.removeClass(this.activeClass);
15262         this.fireEvent("deactivate", this);
15263     },
15264
15265     // private
15266     shouldDeactivate : function(e){
15267         return !this.region || !this.region.contains(e.getPoint());
15268     },
15269
15270     // private
15271     handleClick : function(e){
15272         if(this.hideOnClick){
15273             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15274         }
15275     },
15276
15277     // private
15278     expandMenu : function(autoActivate){
15279         // do nothing
15280     },
15281
15282     // private
15283     hideMenu : function(){
15284         // do nothing
15285     }
15286 });/*
15287  * Based on:
15288  * Ext JS Library 1.1.1
15289  * Copyright(c) 2006-2007, Ext JS, LLC.
15290  *
15291  * Originally Released Under LGPL - original licence link has changed is not relivant.
15292  *
15293  * Fork - LGPL
15294  * <script type="text/javascript">
15295  */
15296  
15297 /**
15298  * @class Roo.menu.Adapter
15299  * @extends Roo.menu.BaseItem
15300  * @abstract
15301  * 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.
15302  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15303  * @constructor
15304  * Creates a new Adapter
15305  * @param {Object} config Configuration options
15306  */
15307 Roo.menu.Adapter = function(component, config){
15308     Roo.menu.Adapter.superclass.constructor.call(this, config);
15309     this.component = component;
15310 };
15311 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15312     // private
15313     canActivate : true,
15314
15315     // private
15316     onRender : function(container, position){
15317         this.component.render(container);
15318         this.el = this.component.getEl();
15319     },
15320
15321     // private
15322     activate : function(){
15323         if(this.disabled){
15324             return false;
15325         }
15326         this.component.focus();
15327         this.fireEvent("activate", this);
15328         return true;
15329     },
15330
15331     // private
15332     deactivate : function(){
15333         this.fireEvent("deactivate", this);
15334     },
15335
15336     // private
15337     disable : function(){
15338         this.component.disable();
15339         Roo.menu.Adapter.superclass.disable.call(this);
15340     },
15341
15342     // private
15343     enable : function(){
15344         this.component.enable();
15345         Roo.menu.Adapter.superclass.enable.call(this);
15346     }
15347 });/*
15348  * Based on:
15349  * Ext JS Library 1.1.1
15350  * Copyright(c) 2006-2007, Ext JS, LLC.
15351  *
15352  * Originally Released Under LGPL - original licence link has changed is not relivant.
15353  *
15354  * Fork - LGPL
15355  * <script type="text/javascript">
15356  */
15357
15358 /**
15359  * @class Roo.menu.TextItem
15360  * @extends Roo.menu.BaseItem
15361  * Adds a static text string to a menu, usually used as either a heading or group separator.
15362  * Note: old style constructor with text is still supported.
15363  * 
15364  * @constructor
15365  * Creates a new TextItem
15366  * @param {Object} cfg Configuration
15367  */
15368 Roo.menu.TextItem = function(cfg){
15369     if (typeof(cfg) == 'string') {
15370         this.text = cfg;
15371     } else {
15372         Roo.apply(this,cfg);
15373     }
15374     
15375     Roo.menu.TextItem.superclass.constructor.call(this);
15376 };
15377
15378 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15379     /**
15380      * @cfg {String} text Text to show on item.
15381      */
15382     text : '',
15383     
15384     /**
15385      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15386      */
15387     hideOnClick : false,
15388     /**
15389      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15390      */
15391     itemCls : "x-menu-text",
15392
15393     // private
15394     onRender : function(){
15395         var s = document.createElement("span");
15396         s.className = this.itemCls;
15397         s.innerHTML = this.text;
15398         this.el = s;
15399         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15400     }
15401 });/*
15402  * Based on:
15403  * Ext JS Library 1.1.1
15404  * Copyright(c) 2006-2007, Ext JS, LLC.
15405  *
15406  * Originally Released Under LGPL - original licence link has changed is not relivant.
15407  *
15408  * Fork - LGPL
15409  * <script type="text/javascript">
15410  */
15411
15412 /**
15413  * @class Roo.menu.Separator
15414  * @extends Roo.menu.BaseItem
15415  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15416  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15417  * @constructor
15418  * @param {Object} config Configuration options
15419  */
15420 Roo.menu.Separator = function(config){
15421     Roo.menu.Separator.superclass.constructor.call(this, config);
15422 };
15423
15424 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15425     /**
15426      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15427      */
15428     itemCls : "x-menu-sep",
15429     /**
15430      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15431      */
15432     hideOnClick : false,
15433
15434     // private
15435     onRender : function(li){
15436         var s = document.createElement("span");
15437         s.className = this.itemCls;
15438         s.innerHTML = "&#160;";
15439         this.el = s;
15440         li.addClass("x-menu-sep-li");
15441         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15442     }
15443 });/*
15444  * Based on:
15445  * Ext JS Library 1.1.1
15446  * Copyright(c) 2006-2007, Ext JS, LLC.
15447  *
15448  * Originally Released Under LGPL - original licence link has changed is not relivant.
15449  *
15450  * Fork - LGPL
15451  * <script type="text/javascript">
15452  */
15453 /**
15454  * @class Roo.menu.Item
15455  * @extends Roo.menu.BaseItem
15456  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15457  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15458  * activation and click handling.
15459  * @constructor
15460  * Creates a new Item
15461  * @param {Object} config Configuration options
15462  */
15463 Roo.menu.Item = function(config){
15464     Roo.menu.Item.superclass.constructor.call(this, config);
15465     if(this.menu){
15466         this.menu = Roo.menu.MenuMgr.get(this.menu);
15467     }
15468 };
15469 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15470     /**
15471      * @cfg {Roo.menu.Menu} menu
15472      * A Sub menu
15473      */
15474     /**
15475      * @cfg {String} text
15476      * The text to show on the menu item.
15477      */
15478     text: '',
15479      /**
15480      * @cfg {String} HTML to render in menu
15481      * The text to show on the menu item (HTML version).
15482      */
15483     html: '',
15484     /**
15485      * @cfg {String} icon
15486      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15487      */
15488     icon: undefined,
15489     /**
15490      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15491      */
15492     itemCls : "x-menu-item",
15493     /**
15494      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15495      */
15496     canActivate : true,
15497     /**
15498      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15499      */
15500     showDelay: 200,
15501     // doc'd in BaseItem
15502     hideDelay: 200,
15503
15504     // private
15505     ctype: "Roo.menu.Item",
15506     
15507     // private
15508     onRender : function(container, position){
15509         var el = document.createElement("a");
15510         el.hideFocus = true;
15511         el.unselectable = "on";
15512         el.href = this.href || "#";
15513         if(this.hrefTarget){
15514             el.target = this.hrefTarget;
15515         }
15516         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15517         
15518         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15519         
15520         el.innerHTML = String.format(
15521                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15522                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15523         this.el = el;
15524         Roo.menu.Item.superclass.onRender.call(this, container, position);
15525     },
15526
15527     /**
15528      * Sets the text to display in this menu item
15529      * @param {String} text The text to display
15530      * @param {Boolean} isHTML true to indicate text is pure html.
15531      */
15532     setText : function(text, isHTML){
15533         if (isHTML) {
15534             this.html = text;
15535         } else {
15536             this.text = text;
15537             this.html = '';
15538         }
15539         if(this.rendered){
15540             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15541      
15542             this.el.update(String.format(
15543                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15544                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15545             this.parentMenu.autoWidth();
15546         }
15547     },
15548
15549     // private
15550     handleClick : function(e){
15551         if(!this.href){ // if no link defined, stop the event automatically
15552             e.stopEvent();
15553         }
15554         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15555     },
15556
15557     // private
15558     activate : function(autoExpand){
15559         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15560             this.focus();
15561             if(autoExpand){
15562                 this.expandMenu();
15563             }
15564         }
15565         return true;
15566     },
15567
15568     // private
15569     shouldDeactivate : function(e){
15570         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15571             if(this.menu && this.menu.isVisible()){
15572                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15573             }
15574             return true;
15575         }
15576         return false;
15577     },
15578
15579     // private
15580     deactivate : function(){
15581         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15582         this.hideMenu();
15583     },
15584
15585     // private
15586     expandMenu : function(autoActivate){
15587         if(!this.disabled && this.menu){
15588             clearTimeout(this.hideTimer);
15589             delete this.hideTimer;
15590             if(!this.menu.isVisible() && !this.showTimer){
15591                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15592             }else if (this.menu.isVisible() && autoActivate){
15593                 this.menu.tryActivate(0, 1);
15594             }
15595         }
15596     },
15597
15598     // private
15599     deferExpand : function(autoActivate){
15600         delete this.showTimer;
15601         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15602         if(autoActivate){
15603             this.menu.tryActivate(0, 1);
15604         }
15605     },
15606
15607     // private
15608     hideMenu : function(){
15609         clearTimeout(this.showTimer);
15610         delete this.showTimer;
15611         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15612             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15613         }
15614     },
15615
15616     // private
15617     deferHide : function(){
15618         delete this.hideTimer;
15619         this.menu.hide();
15620     }
15621 });/*
15622  * Based on:
15623  * Ext JS Library 1.1.1
15624  * Copyright(c) 2006-2007, Ext JS, LLC.
15625  *
15626  * Originally Released Under LGPL - original licence link has changed is not relivant.
15627  *
15628  * Fork - LGPL
15629  * <script type="text/javascript">
15630  */
15631  
15632 /**
15633  * @class Roo.menu.CheckItem
15634  * @extends Roo.menu.Item
15635  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15636  * @constructor
15637  * Creates a new CheckItem
15638  * @param {Object} config Configuration options
15639  */
15640 Roo.menu.CheckItem = function(config){
15641     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15642     this.addEvents({
15643         /**
15644          * @event beforecheckchange
15645          * Fires before the checked value is set, providing an opportunity to cancel if needed
15646          * @param {Roo.menu.CheckItem} this
15647          * @param {Boolean} checked The new checked value that will be set
15648          */
15649         "beforecheckchange" : true,
15650         /**
15651          * @event checkchange
15652          * Fires after the checked value has been set
15653          * @param {Roo.menu.CheckItem} this
15654          * @param {Boolean} checked The checked value that was set
15655          */
15656         "checkchange" : true
15657     });
15658     if(this.checkHandler){
15659         this.on('checkchange', this.checkHandler, this.scope);
15660     }
15661 };
15662 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15663     /**
15664      * @cfg {String} group
15665      * All check items with the same group name will automatically be grouped into a single-select
15666      * radio button group (defaults to '')
15667      */
15668     /**
15669      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15670      */
15671     itemCls : "x-menu-item x-menu-check-item",
15672     /**
15673      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15674      */
15675     groupClass : "x-menu-group-item",
15676
15677     /**
15678      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15679      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15680      * initialized with checked = true will be rendered as checked.
15681      */
15682     checked: false,
15683
15684     // private
15685     ctype: "Roo.menu.CheckItem",
15686
15687     // private
15688     onRender : function(c){
15689         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15690         if(this.group){
15691             this.el.addClass(this.groupClass);
15692         }
15693         Roo.menu.MenuMgr.registerCheckable(this);
15694         if(this.checked){
15695             this.checked = false;
15696             this.setChecked(true, true);
15697         }
15698     },
15699
15700     // private
15701     destroy : function(){
15702         if(this.rendered){
15703             Roo.menu.MenuMgr.unregisterCheckable(this);
15704         }
15705         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15706     },
15707
15708     /**
15709      * Set the checked state of this item
15710      * @param {Boolean} checked The new checked value
15711      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15712      */
15713     setChecked : function(state, suppressEvent){
15714         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15715             if(this.container){
15716                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15717             }
15718             this.checked = state;
15719             if(suppressEvent !== true){
15720                 this.fireEvent("checkchange", this, state);
15721             }
15722         }
15723     },
15724
15725     // private
15726     handleClick : function(e){
15727        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15728            this.setChecked(!this.checked);
15729        }
15730        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15731     }
15732 });/*
15733  * Based on:
15734  * Ext JS Library 1.1.1
15735  * Copyright(c) 2006-2007, Ext JS, LLC.
15736  *
15737  * Originally Released Under LGPL - original licence link has changed is not relivant.
15738  *
15739  * Fork - LGPL
15740  * <script type="text/javascript">
15741  */
15742  
15743 /**
15744  * @class Roo.menu.DateItem
15745  * @extends Roo.menu.Adapter
15746  * A menu item that wraps the {@link Roo.DatPicker} component.
15747  * @constructor
15748  * Creates a new DateItem
15749  * @param {Object} config Configuration options
15750  */
15751 Roo.menu.DateItem = function(config){
15752     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15753     /** The Roo.DatePicker object @type Roo.DatePicker */
15754     this.picker = this.component;
15755     this.addEvents({select: true});
15756     
15757     this.picker.on("render", function(picker){
15758         picker.getEl().swallowEvent("click");
15759         picker.container.addClass("x-menu-date-item");
15760     });
15761
15762     this.picker.on("select", this.onSelect, this);
15763 };
15764
15765 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15766     // private
15767     onSelect : function(picker, date){
15768         this.fireEvent("select", this, date, picker);
15769         Roo.menu.DateItem.superclass.handleClick.call(this);
15770     }
15771 });/*
15772  * Based on:
15773  * Ext JS Library 1.1.1
15774  * Copyright(c) 2006-2007, Ext JS, LLC.
15775  *
15776  * Originally Released Under LGPL - original licence link has changed is not relivant.
15777  *
15778  * Fork - LGPL
15779  * <script type="text/javascript">
15780  */
15781  
15782 /**
15783  * @class Roo.menu.ColorItem
15784  * @extends Roo.menu.Adapter
15785  * A menu item that wraps the {@link Roo.ColorPalette} component.
15786  * @constructor
15787  * Creates a new ColorItem
15788  * @param {Object} config Configuration options
15789  */
15790 Roo.menu.ColorItem = function(config){
15791     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15792     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15793     this.palette = this.component;
15794     this.relayEvents(this.palette, ["select"]);
15795     if(this.selectHandler){
15796         this.on('select', this.selectHandler, this.scope);
15797     }
15798 };
15799 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15800  * Based on:
15801  * Ext JS Library 1.1.1
15802  * Copyright(c) 2006-2007, Ext JS, LLC.
15803  *
15804  * Originally Released Under LGPL - original licence link has changed is not relivant.
15805  *
15806  * Fork - LGPL
15807  * <script type="text/javascript">
15808  */
15809  
15810
15811 /**
15812  * @class Roo.menu.DateMenu
15813  * @extends Roo.menu.Menu
15814  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15815  * @constructor
15816  * Creates a new DateMenu
15817  * @param {Object} config Configuration options
15818  */
15819 Roo.menu.DateMenu = function(config){
15820     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15821     this.plain = true;
15822     var di = new Roo.menu.DateItem(config);
15823     this.add(di);
15824     /**
15825      * The {@link Roo.DatePicker} instance for this DateMenu
15826      * @type DatePicker
15827      */
15828     this.picker = di.picker;
15829     /**
15830      * @event select
15831      * @param {DatePicker} picker
15832      * @param {Date} date
15833      */
15834     this.relayEvents(di, ["select"]);
15835     this.on('beforeshow', function(){
15836         if(this.picker){
15837             this.picker.hideMonthPicker(false);
15838         }
15839     }, this);
15840 };
15841 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15842     cls:'x-date-menu'
15843 });/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853  
15854
15855 /**
15856  * @class Roo.menu.ColorMenu
15857  * @extends Roo.menu.Menu
15858  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15859  * @constructor
15860  * Creates a new ColorMenu
15861  * @param {Object} config Configuration options
15862  */
15863 Roo.menu.ColorMenu = function(config){
15864     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15865     this.plain = true;
15866     var ci = new Roo.menu.ColorItem(config);
15867     this.add(ci);
15868     /**
15869      * The {@link Roo.ColorPalette} instance for this ColorMenu
15870      * @type ColorPalette
15871      */
15872     this.palette = ci.palette;
15873     /**
15874      * @event select
15875      * @param {ColorPalette} palette
15876      * @param {String} color
15877      */
15878     this.relayEvents(ci, ["select"]);
15879 };
15880 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15881  * Based on:
15882  * Ext JS Library 1.1.1
15883  * Copyright(c) 2006-2007, Ext JS, LLC.
15884  *
15885  * Originally Released Under LGPL - original licence link has changed is not relivant.
15886  *
15887  * Fork - LGPL
15888  * <script type="text/javascript">
15889  */
15890  
15891 /**
15892  * @class Roo.form.TextItem
15893  * @extends Roo.BoxComponent
15894  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15895  * @constructor
15896  * Creates a new TextItem
15897  * @param {Object} config Configuration options
15898  */
15899 Roo.form.TextItem = function(config){
15900     Roo.form.TextItem.superclass.constructor.call(this, config);
15901 };
15902
15903 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15904     
15905     /**
15906      * @cfg {String} tag the tag for this item (default div)
15907      */
15908     tag : 'div',
15909     /**
15910      * @cfg {String} html the content for this item
15911      */
15912     html : '',
15913     
15914     getAutoCreate : function()
15915     {
15916         var cfg = {
15917             id: this.id,
15918             tag: this.tag,
15919             html: this.html,
15920             cls: 'x-form-item'
15921         };
15922         
15923         return cfg;
15924         
15925     },
15926     
15927     onRender : function(ct, position)
15928     {
15929         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15930         
15931         if(!this.el){
15932             var cfg = this.getAutoCreate();
15933             if(!cfg.name){
15934                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15935             }
15936             if (!cfg.name.length) {
15937                 delete cfg.name;
15938             }
15939             this.el = ct.createChild(cfg, position);
15940         }
15941     },
15942     /*
15943      * setHTML
15944      * @param {String} html update the Contents of the element.
15945      */
15946     setHTML : function(html)
15947     {
15948         this.fieldEl.dom.innerHTML = html;
15949     }
15950     
15951 });/*
15952  * Based on:
15953  * Ext JS Library 1.1.1
15954  * Copyright(c) 2006-2007, Ext JS, LLC.
15955  *
15956  * Originally Released Under LGPL - original licence link has changed is not relivant.
15957  *
15958  * Fork - LGPL
15959  * <script type="text/javascript">
15960  */
15961  
15962 /**
15963  * @class Roo.form.Field
15964  * @extends Roo.BoxComponent
15965  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15966  * @constructor
15967  * Creates a new Field
15968  * @param {Object} config Configuration options
15969  */
15970 Roo.form.Field = function(config){
15971     Roo.form.Field.superclass.constructor.call(this, config);
15972 };
15973
15974 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15975     /**
15976      * @cfg {String} fieldLabel Label to use when rendering a form.
15977      */
15978        /**
15979      * @cfg {String} qtip Mouse over tip
15980      */
15981      
15982     /**
15983      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15984      */
15985     invalidClass : "x-form-invalid",
15986     /**
15987      * @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")
15988      */
15989     invalidText : "The value in this field is invalid",
15990     /**
15991      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15992      */
15993     focusClass : "x-form-focus",
15994     /**
15995      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15996       automatic validation (defaults to "keyup").
15997      */
15998     validationEvent : "keyup",
15999     /**
16000      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16001      */
16002     validateOnBlur : true,
16003     /**
16004      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16005      */
16006     validationDelay : 250,
16007     /**
16008      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16009      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16010      */
16011     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16012     /**
16013      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16014      */
16015     fieldClass : "x-form-field",
16016     /**
16017      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16018      *<pre>
16019 Value         Description
16020 -----------   ----------------------------------------------------------------------
16021 qtip          Display a quick tip when the user hovers over the field
16022 title         Display a default browser title attribute popup
16023 under         Add a block div beneath the field containing the error text
16024 side          Add an error icon to the right of the field with a popup on hover
16025 [element id]  Add the error text directly to the innerHTML of the specified element
16026 </pre>
16027      */
16028     msgTarget : 'qtip',
16029     /**
16030      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16031      */
16032     msgFx : 'normal',
16033
16034     /**
16035      * @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.
16036      */
16037     readOnly : false,
16038
16039     /**
16040      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16041      */
16042     disabled : false,
16043
16044     /**
16045      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16046      */
16047     inputType : undefined,
16048     
16049     /**
16050      * @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).
16051          */
16052         tabIndex : undefined,
16053         
16054     // private
16055     isFormField : true,
16056
16057     // private
16058     hasFocus : false,
16059     /**
16060      * @property {Roo.Element} fieldEl
16061      * Element Containing the rendered Field (with label etc.)
16062      */
16063     /**
16064      * @cfg {Mixed} value A value to initialize this field with.
16065      */
16066     value : undefined,
16067
16068     /**
16069      * @cfg {String} name The field's HTML name attribute.
16070      */
16071     /**
16072      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16073      */
16074     // private
16075     loadedValue : false,
16076      
16077      
16078         // private ??
16079         initComponent : function(){
16080         Roo.form.Field.superclass.initComponent.call(this);
16081         this.addEvents({
16082             /**
16083              * @event focus
16084              * Fires when this field receives input focus.
16085              * @param {Roo.form.Field} this
16086              */
16087             focus : true,
16088             /**
16089              * @event blur
16090              * Fires when this field loses input focus.
16091              * @param {Roo.form.Field} this
16092              */
16093             blur : true,
16094             /**
16095              * @event specialkey
16096              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16097              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16098              * @param {Roo.form.Field} this
16099              * @param {Roo.EventObject} e The event object
16100              */
16101             specialkey : true,
16102             /**
16103              * @event change
16104              * Fires just before the field blurs if the field value has changed.
16105              * @param {Roo.form.Field} this
16106              * @param {Mixed} newValue The new value
16107              * @param {Mixed} oldValue The original value
16108              */
16109             change : true,
16110             /**
16111              * @event invalid
16112              * Fires after the field has been marked as invalid.
16113              * @param {Roo.form.Field} this
16114              * @param {String} msg The validation message
16115              */
16116             invalid : true,
16117             /**
16118              * @event valid
16119              * Fires after the field has been validated with no errors.
16120              * @param {Roo.form.Field} this
16121              */
16122             valid : true,
16123              /**
16124              * @event keyup
16125              * Fires after the key up
16126              * @param {Roo.form.Field} this
16127              * @param {Roo.EventObject}  e The event Object
16128              */
16129             keyup : true
16130         });
16131     },
16132
16133     /**
16134      * Returns the name attribute of the field if available
16135      * @return {String} name The field name
16136      */
16137     getName: function(){
16138          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16139     },
16140
16141     // private
16142     onRender : function(ct, position){
16143         Roo.form.Field.superclass.onRender.call(this, ct, position);
16144         if(!this.el){
16145             var cfg = this.getAutoCreate();
16146             if(!cfg.name){
16147                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16148             }
16149             if (!cfg.name.length) {
16150                 delete cfg.name;
16151             }
16152             if(this.inputType){
16153                 cfg.type = this.inputType;
16154             }
16155             this.el = ct.createChild(cfg, position);
16156         }
16157         var type = this.el.dom.type;
16158         if(type){
16159             if(type == 'password'){
16160                 type = 'text';
16161             }
16162             this.el.addClass('x-form-'+type);
16163         }
16164         if(this.readOnly){
16165             this.el.dom.readOnly = true;
16166         }
16167         if(this.tabIndex !== undefined){
16168             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16169         }
16170
16171         this.el.addClass([this.fieldClass, this.cls]);
16172         this.initValue();
16173     },
16174
16175     /**
16176      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16177      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16178      * @return {Roo.form.Field} this
16179      */
16180     applyTo : function(target){
16181         this.allowDomMove = false;
16182         this.el = Roo.get(target);
16183         this.render(this.el.dom.parentNode);
16184         return this;
16185     },
16186
16187     // private
16188     initValue : function(){
16189         if(this.value !== undefined){
16190             this.setValue(this.value);
16191         }else if(this.el.dom.value.length > 0){
16192             this.setValue(this.el.dom.value);
16193         }
16194     },
16195
16196     /**
16197      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16198      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16199      */
16200     isDirty : function() {
16201         if(this.disabled) {
16202             return false;
16203         }
16204         return String(this.getValue()) !== String(this.originalValue);
16205     },
16206
16207     /**
16208      * stores the current value in loadedValue
16209      */
16210     resetHasChanged : function()
16211     {
16212         this.loadedValue = String(this.getValue());
16213     },
16214     /**
16215      * checks the current value against the 'loaded' value.
16216      * Note - will return false if 'resetHasChanged' has not been called first.
16217      */
16218     hasChanged : function()
16219     {
16220         if(this.disabled || this.readOnly) {
16221             return false;
16222         }
16223         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16224     },
16225     
16226     
16227     
16228     // private
16229     afterRender : function(){
16230         Roo.form.Field.superclass.afterRender.call(this);
16231         this.initEvents();
16232     },
16233
16234     // private
16235     fireKey : function(e){
16236         //Roo.log('field ' + e.getKey());
16237         if(e.isNavKeyPress()){
16238             this.fireEvent("specialkey", this, e);
16239         }
16240     },
16241
16242     /**
16243      * Resets the current field value to the originally loaded value and clears any validation messages
16244      */
16245     reset : function(){
16246         this.setValue(this.resetValue);
16247         this.originalValue = this.getValue();
16248         this.clearInvalid();
16249     },
16250
16251     // private
16252     initEvents : function(){
16253         // safari killled keypress - so keydown is now used..
16254         this.el.on("keydown" , this.fireKey,  this);
16255         this.el.on("focus", this.onFocus,  this);
16256         this.el.on("blur", this.onBlur,  this);
16257         this.el.relayEvent('keyup', this);
16258
16259         // reference to original value for reset
16260         this.originalValue = this.getValue();
16261         this.resetValue =  this.getValue();
16262     },
16263
16264     // private
16265     onFocus : function(){
16266         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16267             this.el.addClass(this.focusClass);
16268         }
16269         if(!this.hasFocus){
16270             this.hasFocus = true;
16271             this.startValue = this.getValue();
16272             this.fireEvent("focus", this);
16273         }
16274     },
16275
16276     beforeBlur : Roo.emptyFn,
16277
16278     // private
16279     onBlur : function(){
16280         this.beforeBlur();
16281         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16282             this.el.removeClass(this.focusClass);
16283         }
16284         this.hasFocus = false;
16285         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16286             this.validate();
16287         }
16288         var v = this.getValue();
16289         if(String(v) !== String(this.startValue)){
16290             this.fireEvent('change', this, v, this.startValue);
16291         }
16292         this.fireEvent("blur", this);
16293     },
16294
16295     /**
16296      * Returns whether or not the field value is currently valid
16297      * @param {Boolean} preventMark True to disable marking the field invalid
16298      * @return {Boolean} True if the value is valid, else false
16299      */
16300     isValid : function(preventMark){
16301         if(this.disabled){
16302             return true;
16303         }
16304         var restore = this.preventMark;
16305         this.preventMark = preventMark === true;
16306         var v = this.validateValue(this.processValue(this.getRawValue()));
16307         this.preventMark = restore;
16308         return v;
16309     },
16310
16311     /**
16312      * Validates the field value
16313      * @return {Boolean} True if the value is valid, else false
16314      */
16315     validate : function(){
16316         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16317             this.clearInvalid();
16318             return true;
16319         }
16320         return false;
16321     },
16322
16323     processValue : function(value){
16324         return value;
16325     },
16326
16327     // private
16328     // Subclasses should provide the validation implementation by overriding this
16329     validateValue : function(value){
16330         return true;
16331     },
16332
16333     /**
16334      * Mark this field as invalid
16335      * @param {String} msg The validation message
16336      */
16337     markInvalid : function(msg){
16338         if(!this.rendered || this.preventMark){ // not rendered
16339             return;
16340         }
16341         
16342         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16343         
16344         obj.el.addClass(this.invalidClass);
16345         msg = msg || this.invalidText;
16346         switch(this.msgTarget){
16347             case 'qtip':
16348                 obj.el.dom.qtip = msg;
16349                 obj.el.dom.qclass = 'x-form-invalid-tip';
16350                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16351                     Roo.QuickTips.enable();
16352                 }
16353                 break;
16354             case 'title':
16355                 this.el.dom.title = msg;
16356                 break;
16357             case 'under':
16358                 if(!this.errorEl){
16359                     var elp = this.el.findParent('.x-form-element', 5, true);
16360                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16361                     this.errorEl.setWidth(elp.getWidth(true)-20);
16362                 }
16363                 this.errorEl.update(msg);
16364                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16365                 break;
16366             case 'side':
16367                 if(!this.errorIcon){
16368                     var elp = this.el.findParent('.x-form-element', 5, true);
16369                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16370                 }
16371                 this.alignErrorIcon();
16372                 this.errorIcon.dom.qtip = msg;
16373                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16374                 this.errorIcon.show();
16375                 this.on('resize', this.alignErrorIcon, this);
16376                 break;
16377             default:
16378                 var t = Roo.getDom(this.msgTarget);
16379                 t.innerHTML = msg;
16380                 t.style.display = this.msgDisplay;
16381                 break;
16382         }
16383         this.fireEvent('invalid', this, msg);
16384     },
16385
16386     // private
16387     alignErrorIcon : function(){
16388         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16389     },
16390
16391     /**
16392      * Clear any invalid styles/messages for this field
16393      */
16394     clearInvalid : function(){
16395         if(!this.rendered || this.preventMark){ // not rendered
16396             return;
16397         }
16398         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16399         
16400         obj.el.removeClass(this.invalidClass);
16401         switch(this.msgTarget){
16402             case 'qtip':
16403                 obj.el.dom.qtip = '';
16404                 break;
16405             case 'title':
16406                 this.el.dom.title = '';
16407                 break;
16408             case 'under':
16409                 if(this.errorEl){
16410                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16411                 }
16412                 break;
16413             case 'side':
16414                 if(this.errorIcon){
16415                     this.errorIcon.dom.qtip = '';
16416                     this.errorIcon.hide();
16417                     this.un('resize', this.alignErrorIcon, this);
16418                 }
16419                 break;
16420             default:
16421                 var t = Roo.getDom(this.msgTarget);
16422                 t.innerHTML = '';
16423                 t.style.display = 'none';
16424                 break;
16425         }
16426         this.fireEvent('valid', this);
16427     },
16428
16429     /**
16430      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16431      * @return {Mixed} value The field value
16432      */
16433     getRawValue : function(){
16434         var v = this.el.getValue();
16435         
16436         return v;
16437     },
16438
16439     /**
16440      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16441      * @return {Mixed} value The field value
16442      */
16443     getValue : function(){
16444         var v = this.el.getValue();
16445          
16446         return v;
16447     },
16448
16449     /**
16450      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16451      * @param {Mixed} value The value to set
16452      */
16453     setRawValue : function(v){
16454         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16455     },
16456
16457     /**
16458      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16459      * @param {Mixed} value The value to set
16460      */
16461     setValue : function(v){
16462         this.value = v;
16463         if(this.rendered){
16464             this.el.dom.value = (v === null || v === undefined ? '' : v);
16465              this.validate();
16466         }
16467     },
16468
16469     adjustSize : function(w, h){
16470         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16471         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16472         return s;
16473     },
16474
16475     adjustWidth : function(tag, w){
16476         tag = tag.toLowerCase();
16477         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16478             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16479                 if(tag == 'input'){
16480                     return w + 2;
16481                 }
16482                 if(tag == 'textarea'){
16483                     return w-2;
16484                 }
16485             }else if(Roo.isOpera){
16486                 if(tag == 'input'){
16487                     return w + 2;
16488                 }
16489                 if(tag == 'textarea'){
16490                     return w-2;
16491                 }
16492             }
16493         }
16494         return w;
16495     }
16496 });
16497
16498
16499 // anything other than normal should be considered experimental
16500 Roo.form.Field.msgFx = {
16501     normal : {
16502         show: function(msgEl, f){
16503             msgEl.setDisplayed('block');
16504         },
16505
16506         hide : function(msgEl, f){
16507             msgEl.setDisplayed(false).update('');
16508         }
16509     },
16510
16511     slide : {
16512         show: function(msgEl, f){
16513             msgEl.slideIn('t', {stopFx:true});
16514         },
16515
16516         hide : function(msgEl, f){
16517             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16518         }
16519     },
16520
16521     slideRight : {
16522         show: function(msgEl, f){
16523             msgEl.fixDisplay();
16524             msgEl.alignTo(f.el, 'tl-tr');
16525             msgEl.slideIn('l', {stopFx:true});
16526         },
16527
16528         hide : function(msgEl, f){
16529             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16530         }
16531     }
16532 };/*
16533  * Based on:
16534  * Ext JS Library 1.1.1
16535  * Copyright(c) 2006-2007, Ext JS, LLC.
16536  *
16537  * Originally Released Under LGPL - original licence link has changed is not relivant.
16538  *
16539  * Fork - LGPL
16540  * <script type="text/javascript">
16541  */
16542  
16543
16544 /**
16545  * @class Roo.form.TextField
16546  * @extends Roo.form.Field
16547  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16548  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16549  * @constructor
16550  * Creates a new TextField
16551  * @param {Object} config Configuration options
16552  */
16553 Roo.form.TextField = function(config){
16554     Roo.form.TextField.superclass.constructor.call(this, config);
16555     this.addEvents({
16556         /**
16557          * @event autosize
16558          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16559          * according to the default logic, but this event provides a hook for the developer to apply additional
16560          * logic at runtime to resize the field if needed.
16561              * @param {Roo.form.Field} this This text field
16562              * @param {Number} width The new field width
16563              */
16564         autosize : true
16565     });
16566 };
16567
16568 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16569     /**
16570      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16571      */
16572     grow : false,
16573     /**
16574      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16575      */
16576     growMin : 30,
16577     /**
16578      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16579      */
16580     growMax : 800,
16581     /**
16582      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16583      */
16584     vtype : null,
16585     /**
16586      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16587      */
16588     maskRe : null,
16589     /**
16590      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16591      */
16592     disableKeyFilter : false,
16593     /**
16594      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16595      */
16596     allowBlank : true,
16597     /**
16598      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16599      */
16600     minLength : 0,
16601     /**
16602      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16603      */
16604     maxLength : Number.MAX_VALUE,
16605     /**
16606      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16607      */
16608     minLengthText : "The minimum length for this field is {0}",
16609     /**
16610      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16611      */
16612     maxLengthText : "The maximum length for this field is {0}",
16613     /**
16614      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16615      */
16616     selectOnFocus : false,
16617     /**
16618      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16619      */    
16620     allowLeadingSpace : false,
16621     /**
16622      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16623      */
16624     blankText : "This field is required",
16625     /**
16626      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16627      * If available, this function will be called only after the basic validators all return true, and will be passed the
16628      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16629      */
16630     validator : null,
16631     /**
16632      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16633      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16634      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16635      */
16636     regex : null,
16637     /**
16638      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16639      */
16640     regexText : "",
16641     /**
16642      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16643      */
16644     emptyText : null,
16645    
16646
16647     // private
16648     initEvents : function()
16649     {
16650         if (this.emptyText) {
16651             this.el.attr('placeholder', this.emptyText);
16652         }
16653         
16654         Roo.form.TextField.superclass.initEvents.call(this);
16655         if(this.validationEvent == 'keyup'){
16656             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16657             this.el.on('keyup', this.filterValidation, this);
16658         }
16659         else if(this.validationEvent !== false){
16660             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16661         }
16662         
16663         if(this.selectOnFocus){
16664             this.on("focus", this.preFocus, this);
16665         }
16666         if (!this.allowLeadingSpace) {
16667             this.on('blur', this.cleanLeadingSpace, this);
16668         }
16669         
16670         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16671             this.el.on("keypress", this.filterKeys, this);
16672         }
16673         if(this.grow){
16674             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16675             this.el.on("click", this.autoSize,  this);
16676         }
16677         if(this.el.is('input[type=password]') && Roo.isSafari){
16678             this.el.on('keydown', this.SafariOnKeyDown, this);
16679         }
16680     },
16681
16682     processValue : function(value){
16683         if(this.stripCharsRe){
16684             var newValue = value.replace(this.stripCharsRe, '');
16685             if(newValue !== value){
16686                 this.setRawValue(newValue);
16687                 return newValue;
16688             }
16689         }
16690         return value;
16691     },
16692
16693     filterValidation : function(e){
16694         if(!e.isNavKeyPress()){
16695             this.validationTask.delay(this.validationDelay);
16696         }
16697     },
16698
16699     // private
16700     onKeyUp : function(e){
16701         if(!e.isNavKeyPress()){
16702             this.autoSize();
16703         }
16704     },
16705     // private - clean the leading white space
16706     cleanLeadingSpace : function(e)
16707     {
16708         if ( this.inputType == 'file') {
16709             return;
16710         }
16711         
16712         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16713     },
16714     /**
16715      * Resets the current field value to the originally-loaded value and clears any validation messages.
16716      *  
16717      */
16718     reset : function(){
16719         Roo.form.TextField.superclass.reset.call(this);
16720        
16721     }, 
16722     // private
16723     preFocus : function(){
16724         
16725         if(this.selectOnFocus){
16726             this.el.dom.select();
16727         }
16728     },
16729
16730     
16731     // private
16732     filterKeys : function(e){
16733         var k = e.getKey();
16734         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16735             return;
16736         }
16737         var c = e.getCharCode(), cc = String.fromCharCode(c);
16738         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16739             return;
16740         }
16741         if(!this.maskRe.test(cc)){
16742             e.stopEvent();
16743         }
16744     },
16745
16746     setValue : function(v){
16747         
16748         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16749         
16750         this.autoSize();
16751     },
16752
16753     /**
16754      * Validates a value according to the field's validation rules and marks the field as invalid
16755      * if the validation fails
16756      * @param {Mixed} value The value to validate
16757      * @return {Boolean} True if the value is valid, else false
16758      */
16759     validateValue : function(value){
16760         if(value.length < 1)  { // if it's blank
16761              if(this.allowBlank){
16762                 this.clearInvalid();
16763                 return true;
16764              }else{
16765                 this.markInvalid(this.blankText);
16766                 return false;
16767              }
16768         }
16769         if(value.length < this.minLength){
16770             this.markInvalid(String.format(this.minLengthText, this.minLength));
16771             return false;
16772         }
16773         if(value.length > this.maxLength){
16774             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16775             return false;
16776         }
16777         if(this.vtype){
16778             var vt = Roo.form.VTypes;
16779             if(!vt[this.vtype](value, this)){
16780                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16781                 return false;
16782             }
16783         }
16784         if(typeof this.validator == "function"){
16785             var msg = this.validator(value);
16786             if(msg !== true){
16787                 this.markInvalid(msg);
16788                 return false;
16789             }
16790         }
16791         if(this.regex && !this.regex.test(value)){
16792             this.markInvalid(this.regexText);
16793             return false;
16794         }
16795         return true;
16796     },
16797
16798     /**
16799      * Selects text in this field
16800      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16801      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16802      */
16803     selectText : function(start, end){
16804         var v = this.getRawValue();
16805         if(v.length > 0){
16806             start = start === undefined ? 0 : start;
16807             end = end === undefined ? v.length : end;
16808             var d = this.el.dom;
16809             if(d.setSelectionRange){
16810                 d.setSelectionRange(start, end);
16811             }else if(d.createTextRange){
16812                 var range = d.createTextRange();
16813                 range.moveStart("character", start);
16814                 range.moveEnd("character", v.length-end);
16815                 range.select();
16816             }
16817         }
16818     },
16819
16820     /**
16821      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16822      * This only takes effect if grow = true, and fires the autosize event.
16823      */
16824     autoSize : function(){
16825         if(!this.grow || !this.rendered){
16826             return;
16827         }
16828         if(!this.metrics){
16829             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16830         }
16831         var el = this.el;
16832         var v = el.dom.value;
16833         var d = document.createElement('div');
16834         d.appendChild(document.createTextNode(v));
16835         v = d.innerHTML;
16836         d = null;
16837         v += "&#160;";
16838         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16839         this.el.setWidth(w);
16840         this.fireEvent("autosize", this, w);
16841     },
16842     
16843     // private
16844     SafariOnKeyDown : function(event)
16845     {
16846         // this is a workaround for a password hang bug on chrome/ webkit.
16847         
16848         var isSelectAll = false;
16849         
16850         if(this.el.dom.selectionEnd > 0){
16851             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16852         }
16853         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16854             event.preventDefault();
16855             this.setValue('');
16856             return;
16857         }
16858         
16859         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16860             
16861             event.preventDefault();
16862             // this is very hacky as keydown always get's upper case.
16863             
16864             var cc = String.fromCharCode(event.getCharCode());
16865             
16866             
16867             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16868             
16869         }
16870         
16871         
16872     }
16873 });/*
16874  * Based on:
16875  * Ext JS Library 1.1.1
16876  * Copyright(c) 2006-2007, Ext JS, LLC.
16877  *
16878  * Originally Released Under LGPL - original licence link has changed is not relivant.
16879  *
16880  * Fork - LGPL
16881  * <script type="text/javascript">
16882  */
16883  
16884 /**
16885  * @class Roo.form.Hidden
16886  * @extends Roo.form.TextField
16887  * Simple Hidden element used on forms 
16888  * 
16889  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16890  * 
16891  * @constructor
16892  * Creates a new Hidden form element.
16893  * @param {Object} config Configuration options
16894  */
16895
16896
16897
16898 // easy hidden field...
16899 Roo.form.Hidden = function(config){
16900     Roo.form.Hidden.superclass.constructor.call(this, config);
16901 };
16902   
16903 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16904     fieldLabel:      '',
16905     inputType:      'hidden',
16906     width:          50,
16907     allowBlank:     true,
16908     labelSeparator: '',
16909     hidden:         true,
16910     itemCls :       'x-form-item-display-none'
16911
16912
16913 });
16914
16915
16916 /*
16917  * Based on:
16918  * Ext JS Library 1.1.1
16919  * Copyright(c) 2006-2007, Ext JS, LLC.
16920  *
16921  * Originally Released Under LGPL - original licence link has changed is not relivant.
16922  *
16923  * Fork - LGPL
16924  * <script type="text/javascript">
16925  */
16926  
16927 /**
16928  * @class Roo.form.TriggerField
16929  * @extends Roo.form.TextField
16930  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16931  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16932  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16933  * for which you can provide a custom implementation.  For example:
16934  * <pre><code>
16935 var trigger = new Roo.form.TriggerField();
16936 trigger.onTriggerClick = myTriggerFn;
16937 trigger.applyTo('my-field');
16938 </code></pre>
16939  *
16940  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16941  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16942  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16943  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16944  * @constructor
16945  * Create a new TriggerField.
16946  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16947  * to the base TextField)
16948  */
16949 Roo.form.TriggerField = function(config){
16950     this.mimicing = false;
16951     Roo.form.TriggerField.superclass.constructor.call(this, config);
16952 };
16953
16954 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16955     /**
16956      * @cfg {String} triggerClass A CSS class to apply to the trigger
16957      */
16958     /**
16959      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16960      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16961      */
16962     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16963     /**
16964      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16965      */
16966     hideTrigger:false,
16967
16968     /** @cfg {Boolean} grow @hide */
16969     /** @cfg {Number} growMin @hide */
16970     /** @cfg {Number} growMax @hide */
16971
16972     /**
16973      * @hide 
16974      * @method
16975      */
16976     autoSize: Roo.emptyFn,
16977     // private
16978     monitorTab : true,
16979     // private
16980     deferHeight : true,
16981
16982     
16983     actionMode : 'wrap',
16984     // private
16985     onResize : function(w, h){
16986         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16987         if(typeof w == 'number'){
16988             var x = w - this.trigger.getWidth();
16989             this.el.setWidth(this.adjustWidth('input', x));
16990             this.trigger.setStyle('left', x+'px');
16991         }
16992     },
16993
16994     // private
16995     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16996
16997     // private
16998     getResizeEl : function(){
16999         return this.wrap;
17000     },
17001
17002     // private
17003     getPositionEl : function(){
17004         return this.wrap;
17005     },
17006
17007     // private
17008     alignErrorIcon : function(){
17009         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17010     },
17011
17012     // private
17013     onRender : function(ct, position){
17014         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17015         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17016         this.trigger = this.wrap.createChild(this.triggerConfig ||
17017                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17018         if(this.hideTrigger){
17019             this.trigger.setDisplayed(false);
17020         }
17021         this.initTrigger();
17022         if(!this.width){
17023             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17024         }
17025     },
17026
17027     // private
17028     initTrigger : function(){
17029         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17030         this.trigger.addClassOnOver('x-form-trigger-over');
17031         this.trigger.addClassOnClick('x-form-trigger-click');
17032     },
17033
17034     // private
17035     onDestroy : function(){
17036         if(this.trigger){
17037             this.trigger.removeAllListeners();
17038             this.trigger.remove();
17039         }
17040         if(this.wrap){
17041             this.wrap.remove();
17042         }
17043         Roo.form.TriggerField.superclass.onDestroy.call(this);
17044     },
17045
17046     // private
17047     onFocus : function(){
17048         Roo.form.TriggerField.superclass.onFocus.call(this);
17049         if(!this.mimicing){
17050             this.wrap.addClass('x-trigger-wrap-focus');
17051             this.mimicing = true;
17052             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17053             if(this.monitorTab){
17054                 this.el.on("keydown", this.checkTab, this);
17055             }
17056         }
17057     },
17058
17059     // private
17060     checkTab : function(e){
17061         if(e.getKey() == e.TAB){
17062             this.triggerBlur();
17063         }
17064     },
17065
17066     // private
17067     onBlur : function(){
17068         // do nothing
17069     },
17070
17071     // private
17072     mimicBlur : function(e, t){
17073         if(!this.wrap.contains(t) && this.validateBlur()){
17074             this.triggerBlur();
17075         }
17076     },
17077
17078     // private
17079     triggerBlur : function(){
17080         this.mimicing = false;
17081         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17082         if(this.monitorTab){
17083             this.el.un("keydown", this.checkTab, this);
17084         }
17085         this.wrap.removeClass('x-trigger-wrap-focus');
17086         Roo.form.TriggerField.superclass.onBlur.call(this);
17087     },
17088
17089     // private
17090     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17091     validateBlur : function(e, t){
17092         return true;
17093     },
17094
17095     // private
17096     onDisable : function(){
17097         Roo.form.TriggerField.superclass.onDisable.call(this);
17098         if(this.wrap){
17099             this.wrap.addClass('x-item-disabled');
17100         }
17101     },
17102
17103     // private
17104     onEnable : function(){
17105         Roo.form.TriggerField.superclass.onEnable.call(this);
17106         if(this.wrap){
17107             this.wrap.removeClass('x-item-disabled');
17108         }
17109     },
17110
17111     // private
17112     onShow : function(){
17113         var ae = this.getActionEl();
17114         
17115         if(ae){
17116             ae.dom.style.display = '';
17117             ae.dom.style.visibility = 'visible';
17118         }
17119     },
17120
17121     // private
17122     
17123     onHide : function(){
17124         var ae = this.getActionEl();
17125         ae.dom.style.display = 'none';
17126     },
17127
17128     /**
17129      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17130      * by an implementing function.
17131      * @method
17132      * @param {EventObject} e
17133      */
17134     onTriggerClick : Roo.emptyFn
17135 });
17136
17137 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17138 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17139 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17140 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17141     initComponent : function(){
17142         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17143
17144         this.triggerConfig = {
17145             tag:'span', cls:'x-form-twin-triggers', cn:[
17146             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17147             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17148         ]};
17149     },
17150
17151     getTrigger : function(index){
17152         return this.triggers[index];
17153     },
17154
17155     initTrigger : function(){
17156         var ts = this.trigger.select('.x-form-trigger', true);
17157         this.wrap.setStyle('overflow', 'hidden');
17158         var triggerField = this;
17159         ts.each(function(t, all, index){
17160             t.hide = function(){
17161                 var w = triggerField.wrap.getWidth();
17162                 this.dom.style.display = 'none';
17163                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17164             };
17165             t.show = function(){
17166                 var w = triggerField.wrap.getWidth();
17167                 this.dom.style.display = '';
17168                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17169             };
17170             var triggerIndex = 'Trigger'+(index+1);
17171
17172             if(this['hide'+triggerIndex]){
17173                 t.dom.style.display = 'none';
17174             }
17175             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17176             t.addClassOnOver('x-form-trigger-over');
17177             t.addClassOnClick('x-form-trigger-click');
17178         }, this);
17179         this.triggers = ts.elements;
17180     },
17181
17182     onTrigger1Click : Roo.emptyFn,
17183     onTrigger2Click : Roo.emptyFn
17184 });/*
17185  * Based on:
17186  * Ext JS Library 1.1.1
17187  * Copyright(c) 2006-2007, Ext JS, LLC.
17188  *
17189  * Originally Released Under LGPL - original licence link has changed is not relivant.
17190  *
17191  * Fork - LGPL
17192  * <script type="text/javascript">
17193  */
17194  
17195 /**
17196  * @class Roo.form.TextArea
17197  * @extends Roo.form.TextField
17198  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17199  * support for auto-sizing.
17200  * @constructor
17201  * Creates a new TextArea
17202  * @param {Object} config Configuration options
17203  */
17204 Roo.form.TextArea = function(config){
17205     Roo.form.TextArea.superclass.constructor.call(this, config);
17206     // these are provided exchanges for backwards compat
17207     // minHeight/maxHeight were replaced by growMin/growMax to be
17208     // compatible with TextField growing config values
17209     if(this.minHeight !== undefined){
17210         this.growMin = this.minHeight;
17211     }
17212     if(this.maxHeight !== undefined){
17213         this.growMax = this.maxHeight;
17214     }
17215 };
17216
17217 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17218     /**
17219      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17220      */
17221     growMin : 60,
17222     /**
17223      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17224      */
17225     growMax: 1000,
17226     /**
17227      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17228      * in the field (equivalent to setting overflow: hidden, defaults to false)
17229      */
17230     preventScrollbars: false,
17231     /**
17232      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17233      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17234      */
17235
17236     // private
17237     onRender : function(ct, position){
17238         if(!this.el){
17239             this.defaultAutoCreate = {
17240                 tag: "textarea",
17241                 style:"width:300px;height:60px;",
17242                 autocomplete: "new-password"
17243             };
17244         }
17245         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17246         if(this.grow){
17247             this.textSizeEl = Roo.DomHelper.append(document.body, {
17248                 tag: "pre", cls: "x-form-grow-sizer"
17249             });
17250             if(this.preventScrollbars){
17251                 this.el.setStyle("overflow", "hidden");
17252             }
17253             this.el.setHeight(this.growMin);
17254         }
17255     },
17256
17257     onDestroy : function(){
17258         if(this.textSizeEl){
17259             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17260         }
17261         Roo.form.TextArea.superclass.onDestroy.call(this);
17262     },
17263
17264     // private
17265     onKeyUp : function(e){
17266         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17267             this.autoSize();
17268         }
17269     },
17270
17271     /**
17272      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17273      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17274      */
17275     autoSize : function(){
17276         if(!this.grow || !this.textSizeEl){
17277             return;
17278         }
17279         var el = this.el;
17280         var v = el.dom.value;
17281         var ts = this.textSizeEl;
17282
17283         ts.innerHTML = '';
17284         ts.appendChild(document.createTextNode(v));
17285         v = ts.innerHTML;
17286
17287         Roo.fly(ts).setWidth(this.el.getWidth());
17288         if(v.length < 1){
17289             v = "&#160;&#160;";
17290         }else{
17291             if(Roo.isIE){
17292                 v = v.replace(/\n/g, '<p>&#160;</p>');
17293             }
17294             v += "&#160;\n&#160;";
17295         }
17296         ts.innerHTML = v;
17297         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17298         if(h != this.lastHeight){
17299             this.lastHeight = h;
17300             this.el.setHeight(h);
17301             this.fireEvent("autosize", this, h);
17302         }
17303     }
17304 });/*
17305  * Based on:
17306  * Ext JS Library 1.1.1
17307  * Copyright(c) 2006-2007, Ext JS, LLC.
17308  *
17309  * Originally Released Under LGPL - original licence link has changed is not relivant.
17310  *
17311  * Fork - LGPL
17312  * <script type="text/javascript">
17313  */
17314  
17315
17316 /**
17317  * @class Roo.form.NumberField
17318  * @extends Roo.form.TextField
17319  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17320  * @constructor
17321  * Creates a new NumberField
17322  * @param {Object} config Configuration options
17323  */
17324 Roo.form.NumberField = function(config){
17325     Roo.form.NumberField.superclass.constructor.call(this, config);
17326 };
17327
17328 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17329     /**
17330      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17331      */
17332     fieldClass: "x-form-field x-form-num-field",
17333     /**
17334      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17335      */
17336     allowDecimals : true,
17337     /**
17338      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17339      */
17340     decimalSeparator : ".",
17341     /**
17342      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17343      */
17344     decimalPrecision : 2,
17345     /**
17346      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17347      */
17348     allowNegative : true,
17349     /**
17350      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17351      */
17352     minValue : Number.NEGATIVE_INFINITY,
17353     /**
17354      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17355      */
17356     maxValue : Number.MAX_VALUE,
17357     /**
17358      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17359      */
17360     minText : "The minimum value for this field is {0}",
17361     /**
17362      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17363      */
17364     maxText : "The maximum value for this field is {0}",
17365     /**
17366      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17367      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17368      */
17369     nanText : "{0} is not a valid number",
17370
17371     // private
17372     initEvents : function(){
17373         Roo.form.NumberField.superclass.initEvents.call(this);
17374         var allowed = "0123456789";
17375         if(this.allowDecimals){
17376             allowed += this.decimalSeparator;
17377         }
17378         if(this.allowNegative){
17379             allowed += "-";
17380         }
17381         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17382         var keyPress = function(e){
17383             var k = e.getKey();
17384             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17385                 return;
17386             }
17387             var c = e.getCharCode();
17388             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17389                 e.stopEvent();
17390             }
17391         };
17392         this.el.on("keypress", keyPress, this);
17393     },
17394
17395     // private
17396     validateValue : function(value){
17397         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17398             return false;
17399         }
17400         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17401              return true;
17402         }
17403         var num = this.parseValue(value);
17404         if(isNaN(num)){
17405             this.markInvalid(String.format(this.nanText, value));
17406             return false;
17407         }
17408         if(num < this.minValue){
17409             this.markInvalid(String.format(this.minText, this.minValue));
17410             return false;
17411         }
17412         if(num > this.maxValue){
17413             this.markInvalid(String.format(this.maxText, this.maxValue));
17414             return false;
17415         }
17416         return true;
17417     },
17418
17419     getValue : function(){
17420         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17421     },
17422
17423     // private
17424     parseValue : function(value){
17425         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17426         return isNaN(value) ? '' : value;
17427     },
17428
17429     // private
17430     fixPrecision : function(value){
17431         var nan = isNaN(value);
17432         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17433             return nan ? '' : value;
17434         }
17435         return parseFloat(value).toFixed(this.decimalPrecision);
17436     },
17437
17438     setValue : function(v){
17439         v = this.fixPrecision(v);
17440         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17441     },
17442
17443     // private
17444     decimalPrecisionFcn : function(v){
17445         return Math.floor(v);
17446     },
17447
17448     beforeBlur : function(){
17449         var v = this.parseValue(this.getRawValue());
17450         if(v){
17451             this.setValue(v);
17452         }
17453     }
17454 });/*
17455  * Based on:
17456  * Ext JS Library 1.1.1
17457  * Copyright(c) 2006-2007, Ext JS, LLC.
17458  *
17459  * Originally Released Under LGPL - original licence link has changed is not relivant.
17460  *
17461  * Fork - LGPL
17462  * <script type="text/javascript">
17463  */
17464  
17465 /**
17466  * @class Roo.form.DateField
17467  * @extends Roo.form.TriggerField
17468  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17469 * @constructor
17470 * Create a new DateField
17471 * @param {Object} config
17472  */
17473 Roo.form.DateField = function(config)
17474 {
17475     Roo.form.DateField.superclass.constructor.call(this, config);
17476     
17477       this.addEvents({
17478          
17479         /**
17480          * @event select
17481          * Fires when a date is selected
17482              * @param {Roo.form.DateField} combo This combo box
17483              * @param {Date} date The date selected
17484              */
17485         'select' : true
17486          
17487     });
17488     
17489     
17490     if(typeof this.minValue == "string") {
17491         this.minValue = this.parseDate(this.minValue);
17492     }
17493     if(typeof this.maxValue == "string") {
17494         this.maxValue = this.parseDate(this.maxValue);
17495     }
17496     this.ddMatch = null;
17497     if(this.disabledDates){
17498         var dd = this.disabledDates;
17499         var re = "(?:";
17500         for(var i = 0; i < dd.length; i++){
17501             re += dd[i];
17502             if(i != dd.length-1) {
17503                 re += "|";
17504             }
17505         }
17506         this.ddMatch = new RegExp(re + ")");
17507     }
17508 };
17509
17510 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17511     /**
17512      * @cfg {String} format
17513      * The default date format string which can be overriden for localization support.  The format must be
17514      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17515      */
17516     format : "m/d/y",
17517     /**
17518      * @cfg {String} altFormats
17519      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17520      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17521      */
17522     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17523     /**
17524      * @cfg {Array} disabledDays
17525      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17526      */
17527     disabledDays : null,
17528     /**
17529      * @cfg {String} disabledDaysText
17530      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17531      */
17532     disabledDaysText : "Disabled",
17533     /**
17534      * @cfg {Array} disabledDates
17535      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17536      * expression so they are very powerful. Some examples:
17537      * <ul>
17538      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17539      * <li>["03/08", "09/16"] would disable those days for every year</li>
17540      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17541      * <li>["03/../2006"] would disable every day in March 2006</li>
17542      * <li>["^03"] would disable every day in every March</li>
17543      * </ul>
17544      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17545      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17546      */
17547     disabledDates : null,
17548     /**
17549      * @cfg {String} disabledDatesText
17550      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17551      */
17552     disabledDatesText : "Disabled",
17553     /**
17554      * @cfg {Date/String} minValue
17555      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17556      * valid format (defaults to null).
17557      */
17558     minValue : null,
17559     /**
17560      * @cfg {Date/String} maxValue
17561      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17562      * valid format (defaults to null).
17563      */
17564     maxValue : null,
17565     /**
17566      * @cfg {String} minText
17567      * The error text to display when the date in the cell is before minValue (defaults to
17568      * 'The date in this field must be after {minValue}').
17569      */
17570     minText : "The date in this field must be equal to or after {0}",
17571     /**
17572      * @cfg {String} maxText
17573      * The error text to display when the date in the cell is after maxValue (defaults to
17574      * 'The date in this field must be before {maxValue}').
17575      */
17576     maxText : "The date in this field must be equal to or before {0}",
17577     /**
17578      * @cfg {String} invalidText
17579      * The error text to display when the date in the field is invalid (defaults to
17580      * '{value} is not a valid date - it must be in the format {format}').
17581      */
17582     invalidText : "{0} is not a valid date - it must be in the format {1}",
17583     /**
17584      * @cfg {String} triggerClass
17585      * An additional CSS class used to style the trigger button.  The trigger will always get the
17586      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17587      * which displays a calendar icon).
17588      */
17589     triggerClass : 'x-form-date-trigger',
17590     
17591
17592     /**
17593      * @cfg {Boolean} useIso
17594      * if enabled, then the date field will use a hidden field to store the 
17595      * real value as iso formated date. default (false)
17596      */ 
17597     useIso : false,
17598     /**
17599      * @cfg {String/Object} autoCreate
17600      * A DomHelper element spec, or true for a default element spec (defaults to
17601      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17602      */ 
17603     // private
17604     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17605     
17606     // private
17607     hiddenField: false,
17608     
17609     onRender : function(ct, position)
17610     {
17611         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17612         if (this.useIso) {
17613             //this.el.dom.removeAttribute('name'); 
17614             Roo.log("Changing name?");
17615             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17616             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17617                     'before', true);
17618             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17619             // prevent input submission
17620             this.hiddenName = this.name;
17621         }
17622             
17623             
17624     },
17625     
17626     // private
17627     validateValue : function(value)
17628     {
17629         value = this.formatDate(value);
17630         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17631             Roo.log('super failed');
17632             return false;
17633         }
17634         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17635              return true;
17636         }
17637         var svalue = value;
17638         value = this.parseDate(value);
17639         if(!value){
17640             Roo.log('parse date failed' + svalue);
17641             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17642             return false;
17643         }
17644         var time = value.getTime();
17645         if(this.minValue && time < this.minValue.getTime()){
17646             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17647             return false;
17648         }
17649         if(this.maxValue && time > this.maxValue.getTime()){
17650             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17651             return false;
17652         }
17653         if(this.disabledDays){
17654             var day = value.getDay();
17655             for(var i = 0; i < this.disabledDays.length; i++) {
17656                 if(day === this.disabledDays[i]){
17657                     this.markInvalid(this.disabledDaysText);
17658                     return false;
17659                 }
17660             }
17661         }
17662         var fvalue = this.formatDate(value);
17663         if(this.ddMatch && this.ddMatch.test(fvalue)){
17664             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17665             return false;
17666         }
17667         return true;
17668     },
17669
17670     // private
17671     // Provides logic to override the default TriggerField.validateBlur which just returns true
17672     validateBlur : function(){
17673         return !this.menu || !this.menu.isVisible();
17674     },
17675     
17676     getName: function()
17677     {
17678         // returns hidden if it's set..
17679         if (!this.rendered) {return ''};
17680         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17681         
17682     },
17683
17684     /**
17685      * Returns the current date value of the date field.
17686      * @return {Date} The date value
17687      */
17688     getValue : function(){
17689         
17690         return  this.hiddenField ?
17691                 this.hiddenField.value :
17692                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17693     },
17694
17695     /**
17696      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17697      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17698      * (the default format used is "m/d/y").
17699      * <br />Usage:
17700      * <pre><code>
17701 //All of these calls set the same date value (May 4, 2006)
17702
17703 //Pass a date object:
17704 var dt = new Date('5/4/06');
17705 dateField.setValue(dt);
17706
17707 //Pass a date string (default format):
17708 dateField.setValue('5/4/06');
17709
17710 //Pass a date string (custom format):
17711 dateField.format = 'Y-m-d';
17712 dateField.setValue('2006-5-4');
17713 </code></pre>
17714      * @param {String/Date} date The date or valid date string
17715      */
17716     setValue : function(date){
17717         if (this.hiddenField) {
17718             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17719         }
17720         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17721         // make sure the value field is always stored as a date..
17722         this.value = this.parseDate(date);
17723         
17724         
17725     },
17726
17727     // private
17728     parseDate : function(value){
17729         if(!value || value instanceof Date){
17730             return value;
17731         }
17732         var v = Date.parseDate(value, this.format);
17733          if (!v && this.useIso) {
17734             v = Date.parseDate(value, 'Y-m-d');
17735         }
17736         if(!v && this.altFormats){
17737             if(!this.altFormatsArray){
17738                 this.altFormatsArray = this.altFormats.split("|");
17739             }
17740             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17741                 v = Date.parseDate(value, this.altFormatsArray[i]);
17742             }
17743         }
17744         return v;
17745     },
17746
17747     // private
17748     formatDate : function(date, fmt){
17749         return (!date || !(date instanceof Date)) ?
17750                date : date.dateFormat(fmt || this.format);
17751     },
17752
17753     // private
17754     menuListeners : {
17755         select: function(m, d){
17756             
17757             this.setValue(d);
17758             this.fireEvent('select', this, d);
17759         },
17760         show : function(){ // retain focus styling
17761             this.onFocus();
17762         },
17763         hide : function(){
17764             this.focus.defer(10, this);
17765             var ml = this.menuListeners;
17766             this.menu.un("select", ml.select,  this);
17767             this.menu.un("show", ml.show,  this);
17768             this.menu.un("hide", ml.hide,  this);
17769         }
17770     },
17771
17772     // private
17773     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17774     onTriggerClick : function(){
17775         if(this.disabled){
17776             return;
17777         }
17778         if(this.menu == null){
17779             this.menu = new Roo.menu.DateMenu();
17780         }
17781         Roo.apply(this.menu.picker,  {
17782             showClear: this.allowBlank,
17783             minDate : this.minValue,
17784             maxDate : this.maxValue,
17785             disabledDatesRE : this.ddMatch,
17786             disabledDatesText : this.disabledDatesText,
17787             disabledDays : this.disabledDays,
17788             disabledDaysText : this.disabledDaysText,
17789             format : this.useIso ? 'Y-m-d' : this.format,
17790             minText : String.format(this.minText, this.formatDate(this.minValue)),
17791             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17792         });
17793         this.menu.on(Roo.apply({}, this.menuListeners, {
17794             scope:this
17795         }));
17796         this.menu.picker.setValue(this.getValue() || new Date());
17797         this.menu.show(this.el, "tl-bl?");
17798     },
17799
17800     beforeBlur : function(){
17801         var v = this.parseDate(this.getRawValue());
17802         if(v){
17803             this.setValue(v);
17804         }
17805     },
17806
17807     /*@
17808      * overide
17809      * 
17810      */
17811     isDirty : function() {
17812         if(this.disabled) {
17813             return false;
17814         }
17815         
17816         if(typeof(this.startValue) === 'undefined'){
17817             return false;
17818         }
17819         
17820         return String(this.getValue()) !== String(this.startValue);
17821         
17822     },
17823     // @overide
17824     cleanLeadingSpace : function(e)
17825     {
17826        return;
17827     }
17828     
17829 });/*
17830  * Based on:
17831  * Ext JS Library 1.1.1
17832  * Copyright(c) 2006-2007, Ext JS, LLC.
17833  *
17834  * Originally Released Under LGPL - original licence link has changed is not relivant.
17835  *
17836  * Fork - LGPL
17837  * <script type="text/javascript">
17838  */
17839  
17840 /**
17841  * @class Roo.form.MonthField
17842  * @extends Roo.form.TriggerField
17843  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17844 * @constructor
17845 * Create a new MonthField
17846 * @param {Object} config
17847  */
17848 Roo.form.MonthField = function(config){
17849     
17850     Roo.form.MonthField.superclass.constructor.call(this, config);
17851     
17852       this.addEvents({
17853          
17854         /**
17855          * @event select
17856          * Fires when a date is selected
17857              * @param {Roo.form.MonthFieeld} combo This combo box
17858              * @param {Date} date The date selected
17859              */
17860         'select' : true
17861          
17862     });
17863     
17864     
17865     if(typeof this.minValue == "string") {
17866         this.minValue = this.parseDate(this.minValue);
17867     }
17868     if(typeof this.maxValue == "string") {
17869         this.maxValue = this.parseDate(this.maxValue);
17870     }
17871     this.ddMatch = null;
17872     if(this.disabledDates){
17873         var dd = this.disabledDates;
17874         var re = "(?:";
17875         for(var i = 0; i < dd.length; i++){
17876             re += dd[i];
17877             if(i != dd.length-1) {
17878                 re += "|";
17879             }
17880         }
17881         this.ddMatch = new RegExp(re + ")");
17882     }
17883 };
17884
17885 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17886     /**
17887      * @cfg {String} format
17888      * The default date format string which can be overriden for localization support.  The format must be
17889      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17890      */
17891     format : "M Y",
17892     /**
17893      * @cfg {String} altFormats
17894      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17895      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17896      */
17897     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17898     /**
17899      * @cfg {Array} disabledDays
17900      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17901      */
17902     disabledDays : [0,1,2,3,4,5,6],
17903     /**
17904      * @cfg {String} disabledDaysText
17905      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17906      */
17907     disabledDaysText : "Disabled",
17908     /**
17909      * @cfg {Array} disabledDates
17910      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17911      * expression so they are very powerful. Some examples:
17912      * <ul>
17913      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17914      * <li>["03/08", "09/16"] would disable those days for every year</li>
17915      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17916      * <li>["03/../2006"] would disable every day in March 2006</li>
17917      * <li>["^03"] would disable every day in every March</li>
17918      * </ul>
17919      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17920      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17921      */
17922     disabledDates : null,
17923     /**
17924      * @cfg {String} disabledDatesText
17925      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17926      */
17927     disabledDatesText : "Disabled",
17928     /**
17929      * @cfg {Date/String} minValue
17930      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17931      * valid format (defaults to null).
17932      */
17933     minValue : null,
17934     /**
17935      * @cfg {Date/String} maxValue
17936      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17937      * valid format (defaults to null).
17938      */
17939     maxValue : null,
17940     /**
17941      * @cfg {String} minText
17942      * The error text to display when the date in the cell is before minValue (defaults to
17943      * 'The date in this field must be after {minValue}').
17944      */
17945     minText : "The date in this field must be equal to or after {0}",
17946     /**
17947      * @cfg {String} maxTextf
17948      * The error text to display when the date in the cell is after maxValue (defaults to
17949      * 'The date in this field must be before {maxValue}').
17950      */
17951     maxText : "The date in this field must be equal to or before {0}",
17952     /**
17953      * @cfg {String} invalidText
17954      * The error text to display when the date in the field is invalid (defaults to
17955      * '{value} is not a valid date - it must be in the format {format}').
17956      */
17957     invalidText : "{0} is not a valid date - it must be in the format {1}",
17958     /**
17959      * @cfg {String} triggerClass
17960      * An additional CSS class used to style the trigger button.  The trigger will always get the
17961      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17962      * which displays a calendar icon).
17963      */
17964     triggerClass : 'x-form-date-trigger',
17965     
17966
17967     /**
17968      * @cfg {Boolean} useIso
17969      * if enabled, then the date field will use a hidden field to store the 
17970      * real value as iso formated date. default (true)
17971      */ 
17972     useIso : true,
17973     /**
17974      * @cfg {String/Object} autoCreate
17975      * A DomHelper element spec, or true for a default element spec (defaults to
17976      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17977      */ 
17978     // private
17979     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17980     
17981     // private
17982     hiddenField: false,
17983     
17984     hideMonthPicker : false,
17985     
17986     onRender : function(ct, position)
17987     {
17988         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17989         if (this.useIso) {
17990             this.el.dom.removeAttribute('name'); 
17991             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17992                     'before', true);
17993             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17994             // prevent input submission
17995             this.hiddenName = this.name;
17996         }
17997             
17998             
17999     },
18000     
18001     // private
18002     validateValue : function(value)
18003     {
18004         value = this.formatDate(value);
18005         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18006             return false;
18007         }
18008         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18009              return true;
18010         }
18011         var svalue = value;
18012         value = this.parseDate(value);
18013         if(!value){
18014             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18015             return false;
18016         }
18017         var time = value.getTime();
18018         if(this.minValue && time < this.minValue.getTime()){
18019             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18020             return false;
18021         }
18022         if(this.maxValue && time > this.maxValue.getTime()){
18023             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18024             return false;
18025         }
18026         /*if(this.disabledDays){
18027             var day = value.getDay();
18028             for(var i = 0; i < this.disabledDays.length; i++) {
18029                 if(day === this.disabledDays[i]){
18030                     this.markInvalid(this.disabledDaysText);
18031                     return false;
18032                 }
18033             }
18034         }
18035         */
18036         var fvalue = this.formatDate(value);
18037         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18038             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18039             return false;
18040         }
18041         */
18042         return true;
18043     },
18044
18045     // private
18046     // Provides logic to override the default TriggerField.validateBlur which just returns true
18047     validateBlur : function(){
18048         return !this.menu || !this.menu.isVisible();
18049     },
18050
18051     /**
18052      * Returns the current date value of the date field.
18053      * @return {Date} The date value
18054      */
18055     getValue : function(){
18056         
18057         
18058         
18059         return  this.hiddenField ?
18060                 this.hiddenField.value :
18061                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18062     },
18063
18064     /**
18065      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18066      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18067      * (the default format used is "m/d/y").
18068      * <br />Usage:
18069      * <pre><code>
18070 //All of these calls set the same date value (May 4, 2006)
18071
18072 //Pass a date object:
18073 var dt = new Date('5/4/06');
18074 monthField.setValue(dt);
18075
18076 //Pass a date string (default format):
18077 monthField.setValue('5/4/06');
18078
18079 //Pass a date string (custom format):
18080 monthField.format = 'Y-m-d';
18081 monthField.setValue('2006-5-4');
18082 </code></pre>
18083      * @param {String/Date} date The date or valid date string
18084      */
18085     setValue : function(date){
18086         Roo.log('month setValue' + date);
18087         // can only be first of month..
18088         
18089         var val = this.parseDate(date);
18090         
18091         if (this.hiddenField) {
18092             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18093         }
18094         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18095         this.value = this.parseDate(date);
18096     },
18097
18098     // private
18099     parseDate : function(value){
18100         if(!value || value instanceof Date){
18101             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18102             return value;
18103         }
18104         var v = Date.parseDate(value, this.format);
18105         if (!v && this.useIso) {
18106             v = Date.parseDate(value, 'Y-m-d');
18107         }
18108         if (v) {
18109             // 
18110             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18111         }
18112         
18113         
18114         if(!v && this.altFormats){
18115             if(!this.altFormatsArray){
18116                 this.altFormatsArray = this.altFormats.split("|");
18117             }
18118             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18119                 v = Date.parseDate(value, this.altFormatsArray[i]);
18120             }
18121         }
18122         return v;
18123     },
18124
18125     // private
18126     formatDate : function(date, fmt){
18127         return (!date || !(date instanceof Date)) ?
18128                date : date.dateFormat(fmt || this.format);
18129     },
18130
18131     // private
18132     menuListeners : {
18133         select: function(m, d){
18134             this.setValue(d);
18135             this.fireEvent('select', this, d);
18136         },
18137         show : function(){ // retain focus styling
18138             this.onFocus();
18139         },
18140         hide : function(){
18141             this.focus.defer(10, this);
18142             var ml = this.menuListeners;
18143             this.menu.un("select", ml.select,  this);
18144             this.menu.un("show", ml.show,  this);
18145             this.menu.un("hide", ml.hide,  this);
18146         }
18147     },
18148     // private
18149     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18150     onTriggerClick : function(){
18151         if(this.disabled){
18152             return;
18153         }
18154         if(this.menu == null){
18155             this.menu = new Roo.menu.DateMenu();
18156            
18157         }
18158         
18159         Roo.apply(this.menu.picker,  {
18160             
18161             showClear: this.allowBlank,
18162             minDate : this.minValue,
18163             maxDate : this.maxValue,
18164             disabledDatesRE : this.ddMatch,
18165             disabledDatesText : this.disabledDatesText,
18166             
18167             format : this.useIso ? 'Y-m-d' : this.format,
18168             minText : String.format(this.minText, this.formatDate(this.minValue)),
18169             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18170             
18171         });
18172          this.menu.on(Roo.apply({}, this.menuListeners, {
18173             scope:this
18174         }));
18175        
18176         
18177         var m = this.menu;
18178         var p = m.picker;
18179         
18180         // hide month picker get's called when we called by 'before hide';
18181         
18182         var ignorehide = true;
18183         p.hideMonthPicker  = function(disableAnim){
18184             if (ignorehide) {
18185                 return;
18186             }
18187              if(this.monthPicker){
18188                 Roo.log("hideMonthPicker called");
18189                 if(disableAnim === true){
18190                     this.monthPicker.hide();
18191                 }else{
18192                     this.monthPicker.slideOut('t', {duration:.2});
18193                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18194                     p.fireEvent("select", this, this.value);
18195                     m.hide();
18196                 }
18197             }
18198         }
18199         
18200         Roo.log('picker set value');
18201         Roo.log(this.getValue());
18202         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18203         m.show(this.el, 'tl-bl?');
18204         ignorehide  = false;
18205         // this will trigger hideMonthPicker..
18206         
18207         
18208         // hidden the day picker
18209         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18210         
18211         
18212         
18213       
18214         
18215         p.showMonthPicker.defer(100, p);
18216     
18217         
18218        
18219     },
18220
18221     beforeBlur : function(){
18222         var v = this.parseDate(this.getRawValue());
18223         if(v){
18224             this.setValue(v);
18225         }
18226     }
18227
18228     /** @cfg {Boolean} grow @hide */
18229     /** @cfg {Number} growMin @hide */
18230     /** @cfg {Number} growMax @hide */
18231     /**
18232      * @hide
18233      * @method autoSize
18234      */
18235 });/*
18236  * Based on:
18237  * Ext JS Library 1.1.1
18238  * Copyright(c) 2006-2007, Ext JS, LLC.
18239  *
18240  * Originally Released Under LGPL - original licence link has changed is not relivant.
18241  *
18242  * Fork - LGPL
18243  * <script type="text/javascript">
18244  */
18245  
18246
18247 /**
18248  * @class Roo.form.ComboBox
18249  * @extends Roo.form.TriggerField
18250  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18251  * @constructor
18252  * Create a new ComboBox.
18253  * @param {Object} config Configuration options
18254  */
18255 Roo.form.ComboBox = function(config){
18256     Roo.form.ComboBox.superclass.constructor.call(this, config);
18257     this.addEvents({
18258         /**
18259          * @event expand
18260          * Fires when the dropdown list is expanded
18261              * @param {Roo.form.ComboBox} combo This combo box
18262              */
18263         'expand' : true,
18264         /**
18265          * @event collapse
18266          * Fires when the dropdown list is collapsed
18267              * @param {Roo.form.ComboBox} combo This combo box
18268              */
18269         'collapse' : true,
18270         /**
18271          * @event beforeselect
18272          * Fires before a list item is selected. Return false to cancel the selection.
18273              * @param {Roo.form.ComboBox} combo This combo box
18274              * @param {Roo.data.Record} record The data record returned from the underlying store
18275              * @param {Number} index The index of the selected item in the dropdown list
18276              */
18277         'beforeselect' : true,
18278         /**
18279          * @event select
18280          * Fires when a list item is selected
18281              * @param {Roo.form.ComboBox} combo This combo box
18282              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18283              * @param {Number} index The index of the selected item in the dropdown list
18284              */
18285         'select' : true,
18286         /**
18287          * @event beforequery
18288          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18289          * The event object passed has these properties:
18290              * @param {Roo.form.ComboBox} combo This combo box
18291              * @param {String} query The query
18292              * @param {Boolean} forceAll true to force "all" query
18293              * @param {Boolean} cancel true to cancel the query
18294              * @param {Object} e The query event object
18295              */
18296         'beforequery': true,
18297          /**
18298          * @event add
18299          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18300              * @param {Roo.form.ComboBox} combo This combo box
18301              */
18302         'add' : true,
18303         /**
18304          * @event edit
18305          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18306              * @param {Roo.form.ComboBox} combo This combo box
18307              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18308              */
18309         'edit' : true
18310         
18311         
18312     });
18313     if(this.transform){
18314         this.allowDomMove = false;
18315         var s = Roo.getDom(this.transform);
18316         if(!this.hiddenName){
18317             this.hiddenName = s.name;
18318         }
18319         if(!this.store){
18320             this.mode = 'local';
18321             var d = [], opts = s.options;
18322             for(var i = 0, len = opts.length;i < len; i++){
18323                 var o = opts[i];
18324                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18325                 if(o.selected) {
18326                     this.value = value;
18327                 }
18328                 d.push([value, o.text]);
18329             }
18330             this.store = new Roo.data.SimpleStore({
18331                 'id': 0,
18332                 fields: ['value', 'text'],
18333                 data : d
18334             });
18335             this.valueField = 'value';
18336             this.displayField = 'text';
18337         }
18338         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18339         if(!this.lazyRender){
18340             this.target = true;
18341             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18342             s.parentNode.removeChild(s); // remove it
18343             this.render(this.el.parentNode);
18344         }else{
18345             s.parentNode.removeChild(s); // remove it
18346         }
18347
18348     }
18349     if (this.store) {
18350         this.store = Roo.factory(this.store, Roo.data);
18351     }
18352     
18353     this.selectedIndex = -1;
18354     if(this.mode == 'local'){
18355         if(config.queryDelay === undefined){
18356             this.queryDelay = 10;
18357         }
18358         if(config.minChars === undefined){
18359             this.minChars = 0;
18360         }
18361     }
18362 };
18363
18364 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18365     /**
18366      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18367      */
18368     /**
18369      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18370      * rendering into an Roo.Editor, defaults to false)
18371      */
18372     /**
18373      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18374      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18375      */
18376     /**
18377      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18378      */
18379     /**
18380      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18381      * the dropdown list (defaults to undefined, with no header element)
18382      */
18383
18384      /**
18385      * @cfg {String/Roo.Template} tpl The template to use to render the output
18386      */
18387      
18388     // private
18389     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18390     /**
18391      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18392      */
18393     listWidth: undefined,
18394     /**
18395      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18396      * mode = 'remote' or 'text' if mode = 'local')
18397      */
18398     displayField: undefined,
18399     /**
18400      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18401      * mode = 'remote' or 'value' if mode = 'local'). 
18402      * Note: use of a valueField requires the user make a selection
18403      * in order for a value to be mapped.
18404      */
18405     valueField: undefined,
18406     
18407     
18408     /**
18409      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18410      * field's data value (defaults to the underlying DOM element's name)
18411      */
18412     hiddenName: undefined,
18413     /**
18414      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18415      */
18416     listClass: '',
18417     /**
18418      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18419      */
18420     selectedClass: 'x-combo-selected',
18421     /**
18422      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18423      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18424      * which displays a downward arrow icon).
18425      */
18426     triggerClass : 'x-form-arrow-trigger',
18427     /**
18428      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18429      */
18430     shadow:'sides',
18431     /**
18432      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18433      * anchor positions (defaults to 'tl-bl')
18434      */
18435     listAlign: 'tl-bl?',
18436     /**
18437      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18438      */
18439     maxHeight: 300,
18440     /**
18441      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18442      * query specified by the allQuery config option (defaults to 'query')
18443      */
18444     triggerAction: 'query',
18445     /**
18446      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18447      * (defaults to 4, does not apply if editable = false)
18448      */
18449     minChars : 4,
18450     /**
18451      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18452      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18453      */
18454     typeAhead: false,
18455     /**
18456      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18457      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18458      */
18459     queryDelay: 500,
18460     /**
18461      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18462      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18463      */
18464     pageSize: 0,
18465     /**
18466      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18467      * when editable = true (defaults to false)
18468      */
18469     selectOnFocus:false,
18470     /**
18471      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18472      */
18473     queryParam: 'query',
18474     /**
18475      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18476      * when mode = 'remote' (defaults to 'Loading...')
18477      */
18478     loadingText: 'Loading...',
18479     /**
18480      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18481      */
18482     resizable: false,
18483     /**
18484      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18485      */
18486     handleHeight : 8,
18487     /**
18488      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18489      * traditional select (defaults to true)
18490      */
18491     editable: true,
18492     /**
18493      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18494      */
18495     allQuery: '',
18496     /**
18497      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18498      */
18499     mode: 'remote',
18500     /**
18501      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18502      * listWidth has a higher value)
18503      */
18504     minListWidth : 70,
18505     /**
18506      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18507      * allow the user to set arbitrary text into the field (defaults to false)
18508      */
18509     forceSelection:false,
18510     /**
18511      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18512      * if typeAhead = true (defaults to 250)
18513      */
18514     typeAheadDelay : 250,
18515     /**
18516      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18517      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18518      */
18519     valueNotFoundText : undefined,
18520     /**
18521      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18522      */
18523     blockFocus : false,
18524     
18525     /**
18526      * @cfg {Boolean} disableClear Disable showing of clear button.
18527      */
18528     disableClear : false,
18529     /**
18530      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18531      */
18532     alwaysQuery : false,
18533     
18534     //private
18535     addicon : false,
18536     editicon: false,
18537     
18538     // element that contains real text value.. (when hidden is used..)
18539      
18540     // private
18541     onRender : function(ct, position)
18542     {
18543         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18544         
18545         if(this.hiddenName){
18546             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18547                     'before', true);
18548             this.hiddenField.value =
18549                 this.hiddenValue !== undefined ? this.hiddenValue :
18550                 this.value !== undefined ? this.value : '';
18551
18552             // prevent input submission
18553             this.el.dom.removeAttribute('name');
18554              
18555              
18556         }
18557         
18558         if(Roo.isGecko){
18559             this.el.dom.setAttribute('autocomplete', 'off');
18560         }
18561
18562         var cls = 'x-combo-list';
18563
18564         this.list = new Roo.Layer({
18565             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18566         });
18567
18568         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18569         this.list.setWidth(lw);
18570         this.list.swallowEvent('mousewheel');
18571         this.assetHeight = 0;
18572
18573         if(this.title){
18574             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18575             this.assetHeight += this.header.getHeight();
18576         }
18577
18578         this.innerList = this.list.createChild({cls:cls+'-inner'});
18579         this.innerList.on('mouseover', this.onViewOver, this);
18580         this.innerList.on('mousemove', this.onViewMove, this);
18581         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18582         
18583         if(this.allowBlank && !this.pageSize && !this.disableClear){
18584             this.footer = this.list.createChild({cls:cls+'-ft'});
18585             this.pageTb = new Roo.Toolbar(this.footer);
18586            
18587         }
18588         if(this.pageSize){
18589             this.footer = this.list.createChild({cls:cls+'-ft'});
18590             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18591                     {pageSize: this.pageSize});
18592             
18593         }
18594         
18595         if (this.pageTb && this.allowBlank && !this.disableClear) {
18596             var _this = this;
18597             this.pageTb.add(new Roo.Toolbar.Fill(), {
18598                 cls: 'x-btn-icon x-btn-clear',
18599                 text: '&#160;',
18600                 handler: function()
18601                 {
18602                     _this.collapse();
18603                     _this.clearValue();
18604                     _this.onSelect(false, -1);
18605                 }
18606             });
18607         }
18608         if (this.footer) {
18609             this.assetHeight += this.footer.getHeight();
18610         }
18611         
18612
18613         if(!this.tpl){
18614             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18615         }
18616
18617         this.view = new Roo.View(this.innerList, this.tpl, {
18618             singleSelect:true,
18619             store: this.store,
18620             selectedClass: this.selectedClass
18621         });
18622
18623         this.view.on('click', this.onViewClick, this);
18624
18625         this.store.on('beforeload', this.onBeforeLoad, this);
18626         this.store.on('load', this.onLoad, this);
18627         this.store.on('loadexception', this.onLoadException, this);
18628
18629         if(this.resizable){
18630             this.resizer = new Roo.Resizable(this.list,  {
18631                pinned:true, handles:'se'
18632             });
18633             this.resizer.on('resize', function(r, w, h){
18634                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18635                 this.listWidth = w;
18636                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18637                 this.restrictHeight();
18638             }, this);
18639             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18640         }
18641         if(!this.editable){
18642             this.editable = true;
18643             this.setEditable(false);
18644         }  
18645         
18646         
18647         if (typeof(this.events.add.listeners) != 'undefined') {
18648             
18649             this.addicon = this.wrap.createChild(
18650                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18651        
18652             this.addicon.on('click', function(e) {
18653                 this.fireEvent('add', this);
18654             }, this);
18655         }
18656         if (typeof(this.events.edit.listeners) != 'undefined') {
18657             
18658             this.editicon = this.wrap.createChild(
18659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18660             if (this.addicon) {
18661                 this.editicon.setStyle('margin-left', '40px');
18662             }
18663             this.editicon.on('click', function(e) {
18664                 
18665                 // we fire even  if inothing is selected..
18666                 this.fireEvent('edit', this, this.lastData );
18667                 
18668             }, this);
18669         }
18670         
18671         
18672         
18673     },
18674
18675     // private
18676     initEvents : function(){
18677         Roo.form.ComboBox.superclass.initEvents.call(this);
18678
18679         this.keyNav = new Roo.KeyNav(this.el, {
18680             "up" : function(e){
18681                 this.inKeyMode = true;
18682                 this.selectPrev();
18683             },
18684
18685             "down" : function(e){
18686                 if(!this.isExpanded()){
18687                     this.onTriggerClick();
18688                 }else{
18689                     this.inKeyMode = true;
18690                     this.selectNext();
18691                 }
18692             },
18693
18694             "enter" : function(e){
18695                 this.onViewClick();
18696                 //return true;
18697             },
18698
18699             "esc" : function(e){
18700                 this.collapse();
18701             },
18702
18703             "tab" : function(e){
18704                 this.onViewClick(false);
18705                 this.fireEvent("specialkey", this, e);
18706                 return true;
18707             },
18708
18709             scope : this,
18710
18711             doRelay : function(foo, bar, hname){
18712                 if(hname == 'down' || this.scope.isExpanded()){
18713                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18714                 }
18715                 return true;
18716             },
18717
18718             forceKeyDown: true
18719         });
18720         this.queryDelay = Math.max(this.queryDelay || 10,
18721                 this.mode == 'local' ? 10 : 250);
18722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18723         if(this.typeAhead){
18724             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18725         }
18726         if(this.editable !== false){
18727             this.el.on("keyup", this.onKeyUp, this);
18728         }
18729         if(this.forceSelection){
18730             this.on('blur', this.doForce, this);
18731         }
18732     },
18733
18734     onDestroy : function(){
18735         if(this.view){
18736             this.view.setStore(null);
18737             this.view.el.removeAllListeners();
18738             this.view.el.remove();
18739             this.view.purgeListeners();
18740         }
18741         if(this.list){
18742             this.list.destroy();
18743         }
18744         if(this.store){
18745             this.store.un('beforeload', this.onBeforeLoad, this);
18746             this.store.un('load', this.onLoad, this);
18747             this.store.un('loadexception', this.onLoadException, this);
18748         }
18749         Roo.form.ComboBox.superclass.onDestroy.call(this);
18750     },
18751
18752     // private
18753     fireKey : function(e){
18754         if(e.isNavKeyPress() && !this.list.isVisible()){
18755             this.fireEvent("specialkey", this, e);
18756         }
18757     },
18758
18759     // private
18760     onResize: function(w, h){
18761         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18762         
18763         if(typeof w != 'number'){
18764             // we do not handle it!?!?
18765             return;
18766         }
18767         var tw = this.trigger.getWidth();
18768         tw += this.addicon ? this.addicon.getWidth() : 0;
18769         tw += this.editicon ? this.editicon.getWidth() : 0;
18770         var x = w - tw;
18771         this.el.setWidth( this.adjustWidth('input', x));
18772             
18773         this.trigger.setStyle('left', x+'px');
18774         
18775         if(this.list && this.listWidth === undefined){
18776             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18777             this.list.setWidth(lw);
18778             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18779         }
18780         
18781     
18782         
18783     },
18784
18785     /**
18786      * Allow or prevent the user from directly editing the field text.  If false is passed,
18787      * the user will only be able to select from the items defined in the dropdown list.  This method
18788      * is the runtime equivalent of setting the 'editable' config option at config time.
18789      * @param {Boolean} value True to allow the user to directly edit the field text
18790      */
18791     setEditable : function(value){
18792         if(value == this.editable){
18793             return;
18794         }
18795         this.editable = value;
18796         if(!value){
18797             this.el.dom.setAttribute('readOnly', true);
18798             this.el.on('mousedown', this.onTriggerClick,  this);
18799             this.el.addClass('x-combo-noedit');
18800         }else{
18801             this.el.dom.setAttribute('readOnly', false);
18802             this.el.un('mousedown', this.onTriggerClick,  this);
18803             this.el.removeClass('x-combo-noedit');
18804         }
18805     },
18806
18807     // private
18808     onBeforeLoad : function(){
18809         if(!this.hasFocus){
18810             return;
18811         }
18812         this.innerList.update(this.loadingText ?
18813                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18814         this.restrictHeight();
18815         this.selectedIndex = -1;
18816     },
18817
18818     // private
18819     onLoad : function(){
18820         if(!this.hasFocus){
18821             return;
18822         }
18823         if(this.store.getCount() > 0){
18824             this.expand();
18825             this.restrictHeight();
18826             if(this.lastQuery == this.allQuery){
18827                 if(this.editable){
18828                     this.el.dom.select();
18829                 }
18830                 if(!this.selectByValue(this.value, true)){
18831                     this.select(0, true);
18832                 }
18833             }else{
18834                 this.selectNext();
18835                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18836                     this.taTask.delay(this.typeAheadDelay);
18837                 }
18838             }
18839         }else{
18840             this.onEmptyResults();
18841         }
18842         //this.el.focus();
18843     },
18844     // private
18845     onLoadException : function()
18846     {
18847         this.collapse();
18848         Roo.log(this.store.reader.jsonData);
18849         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18850             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18851         }
18852         
18853         
18854     },
18855     // private
18856     onTypeAhead : function(){
18857         if(this.store.getCount() > 0){
18858             var r = this.store.getAt(0);
18859             var newValue = r.data[this.displayField];
18860             var len = newValue.length;
18861             var selStart = this.getRawValue().length;
18862             if(selStart != len){
18863                 this.setRawValue(newValue);
18864                 this.selectText(selStart, newValue.length);
18865             }
18866         }
18867     },
18868
18869     // private
18870     onSelect : function(record, index){
18871         if(this.fireEvent('beforeselect', this, record, index) !== false){
18872             this.setFromData(index > -1 ? record.data : false);
18873             this.collapse();
18874             this.fireEvent('select', this, record, index);
18875         }
18876     },
18877
18878     /**
18879      * Returns the currently selected field value or empty string if no value is set.
18880      * @return {String} value The selected value
18881      */
18882     getValue : function(){
18883         if(this.valueField){
18884             return typeof this.value != 'undefined' ? this.value : '';
18885         }
18886         return Roo.form.ComboBox.superclass.getValue.call(this);
18887     },
18888
18889     /**
18890      * Clears any text/value currently set in the field
18891      */
18892     clearValue : function(){
18893         if(this.hiddenField){
18894             this.hiddenField.value = '';
18895         }
18896         this.value = '';
18897         this.setRawValue('');
18898         this.lastSelectionText = '';
18899         
18900     },
18901
18902     /**
18903      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18904      * will be displayed in the field.  If the value does not match the data value of an existing item,
18905      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18906      * Otherwise the field will be blank (although the value will still be set).
18907      * @param {String} value The value to match
18908      */
18909     setValue : function(v){
18910         var text = v;
18911         if(this.valueField){
18912             var r = this.findRecord(this.valueField, v);
18913             if(r){
18914                 text = r.data[this.displayField];
18915             }else if(this.valueNotFoundText !== undefined){
18916                 text = this.valueNotFoundText;
18917             }
18918         }
18919         this.lastSelectionText = text;
18920         if(this.hiddenField){
18921             this.hiddenField.value = v;
18922         }
18923         Roo.form.ComboBox.superclass.setValue.call(this, text);
18924         this.value = v;
18925     },
18926     /**
18927      * @property {Object} the last set data for the element
18928      */
18929     
18930     lastData : false,
18931     /**
18932      * Sets the value of the field based on a object which is related to the record format for the store.
18933      * @param {Object} value the value to set as. or false on reset?
18934      */
18935     setFromData : function(o){
18936         var dv = ''; // display value
18937         var vv = ''; // value value..
18938         this.lastData = o;
18939         if (this.displayField) {
18940             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18941         } else {
18942             // this is an error condition!!!
18943             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18944         }
18945         
18946         if(this.valueField){
18947             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18948         }
18949         if(this.hiddenField){
18950             this.hiddenField.value = vv;
18951             
18952             this.lastSelectionText = dv;
18953             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18954             this.value = vv;
18955             return;
18956         }
18957         // no hidden field.. - we store the value in 'value', but still display
18958         // display field!!!!
18959         this.lastSelectionText = dv;
18960         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18961         this.value = vv;
18962         
18963         
18964     },
18965     // private
18966     reset : function(){
18967         // overridden so that last data is reset..
18968         this.setValue(this.resetValue);
18969         this.originalValue = this.getValue();
18970         this.clearInvalid();
18971         this.lastData = false;
18972         if (this.view) {
18973             this.view.clearSelections();
18974         }
18975     },
18976     // private
18977     findRecord : function(prop, value){
18978         var record;
18979         if(this.store.getCount() > 0){
18980             this.store.each(function(r){
18981                 if(r.data[prop] == value){
18982                     record = r;
18983                     return false;
18984                 }
18985                 return true;
18986             });
18987         }
18988         return record;
18989     },
18990     
18991     getName: function()
18992     {
18993         // returns hidden if it's set..
18994         if (!this.rendered) {return ''};
18995         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18996         
18997     },
18998     // private
18999     onViewMove : function(e, t){
19000         this.inKeyMode = false;
19001     },
19002
19003     // private
19004     onViewOver : function(e, t){
19005         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19006             return;
19007         }
19008         var item = this.view.findItemFromChild(t);
19009         if(item){
19010             var index = this.view.indexOf(item);
19011             this.select(index, false);
19012         }
19013     },
19014
19015     // private
19016     onViewClick : function(doFocus)
19017     {
19018         var index = this.view.getSelectedIndexes()[0];
19019         var r = this.store.getAt(index);
19020         if(r){
19021             this.onSelect(r, index);
19022         }
19023         if(doFocus !== false && !this.blockFocus){
19024             this.el.focus();
19025         }
19026     },
19027
19028     // private
19029     restrictHeight : function(){
19030         this.innerList.dom.style.height = '';
19031         var inner = this.innerList.dom;
19032         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19033         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19034         this.list.beginUpdate();
19035         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19036         this.list.alignTo(this.el, this.listAlign);
19037         this.list.endUpdate();
19038     },
19039
19040     // private
19041     onEmptyResults : function(){
19042         this.collapse();
19043     },
19044
19045     /**
19046      * Returns true if the dropdown list is expanded, else false.
19047      */
19048     isExpanded : function(){
19049         return this.list.isVisible();
19050     },
19051
19052     /**
19053      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19054      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19055      * @param {String} value The data value of the item to select
19056      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19057      * selected item if it is not currently in view (defaults to true)
19058      * @return {Boolean} True if the value matched an item in the list, else false
19059      */
19060     selectByValue : function(v, scrollIntoView){
19061         if(v !== undefined && v !== null){
19062             var r = this.findRecord(this.valueField || this.displayField, v);
19063             if(r){
19064                 this.select(this.store.indexOf(r), scrollIntoView);
19065                 return true;
19066             }
19067         }
19068         return false;
19069     },
19070
19071     /**
19072      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19074      * @param {Number} index The zero-based index of the list item to select
19075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19076      * selected item if it is not currently in view (defaults to true)
19077      */
19078     select : function(index, scrollIntoView){
19079         this.selectedIndex = index;
19080         this.view.select(index);
19081         if(scrollIntoView !== false){
19082             var el = this.view.getNode(index);
19083             if(el){
19084                 this.innerList.scrollChildIntoView(el, false);
19085             }
19086         }
19087     },
19088
19089     // private
19090     selectNext : function(){
19091         var ct = this.store.getCount();
19092         if(ct > 0){
19093             if(this.selectedIndex == -1){
19094                 this.select(0);
19095             }else if(this.selectedIndex < ct-1){
19096                 this.select(this.selectedIndex+1);
19097             }
19098         }
19099     },
19100
19101     // private
19102     selectPrev : function(){
19103         var ct = this.store.getCount();
19104         if(ct > 0){
19105             if(this.selectedIndex == -1){
19106                 this.select(0);
19107             }else if(this.selectedIndex != 0){
19108                 this.select(this.selectedIndex-1);
19109             }
19110         }
19111     },
19112
19113     // private
19114     onKeyUp : function(e){
19115         if(this.editable !== false && !e.isSpecialKey()){
19116             this.lastKey = e.getKey();
19117             this.dqTask.delay(this.queryDelay);
19118         }
19119     },
19120
19121     // private
19122     validateBlur : function(){
19123         return !this.list || !this.list.isVisible();   
19124     },
19125
19126     // private
19127     initQuery : function(){
19128         this.doQuery(this.getRawValue());
19129     },
19130
19131     // private
19132     doForce : function(){
19133         if(this.el.dom.value.length > 0){
19134             this.el.dom.value =
19135                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19136              
19137         }
19138     },
19139
19140     /**
19141      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19142      * query allowing the query action to be canceled if needed.
19143      * @param {String} query The SQL query to execute
19144      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19145      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19146      * saved in the current store (defaults to false)
19147      */
19148     doQuery : function(q, forceAll){
19149         if(q === undefined || q === null){
19150             q = '';
19151         }
19152         var qe = {
19153             query: q,
19154             forceAll: forceAll,
19155             combo: this,
19156             cancel:false
19157         };
19158         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19159             return false;
19160         }
19161         q = qe.query;
19162         forceAll = qe.forceAll;
19163         if(forceAll === true || (q.length >= this.minChars)){
19164             if(this.lastQuery != q || this.alwaysQuery){
19165                 this.lastQuery = q;
19166                 if(this.mode == 'local'){
19167                     this.selectedIndex = -1;
19168                     if(forceAll){
19169                         this.store.clearFilter();
19170                     }else{
19171                         this.store.filter(this.displayField, q);
19172                     }
19173                     this.onLoad();
19174                 }else{
19175                     this.store.baseParams[this.queryParam] = q;
19176                     this.store.load({
19177                         params: this.getParams(q)
19178                     });
19179                     this.expand();
19180                 }
19181             }else{
19182                 this.selectedIndex = -1;
19183                 this.onLoad();   
19184             }
19185         }
19186     },
19187
19188     // private
19189     getParams : function(q){
19190         var p = {};
19191         //p[this.queryParam] = q;
19192         if(this.pageSize){
19193             p.start = 0;
19194             p.limit = this.pageSize;
19195         }
19196         return p;
19197     },
19198
19199     /**
19200      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19201      */
19202     collapse : function(){
19203         if(!this.isExpanded()){
19204             return;
19205         }
19206         this.list.hide();
19207         Roo.get(document).un('mousedown', this.collapseIf, this);
19208         Roo.get(document).un('mousewheel', this.collapseIf, this);
19209         if (!this.editable) {
19210             Roo.get(document).un('keydown', this.listKeyPress, this);
19211         }
19212         this.fireEvent('collapse', this);
19213     },
19214
19215     // private
19216     collapseIf : function(e){
19217         if(!e.within(this.wrap) && !e.within(this.list)){
19218             this.collapse();
19219         }
19220     },
19221
19222     /**
19223      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19224      */
19225     expand : function(){
19226         if(this.isExpanded() || !this.hasFocus){
19227             return;
19228         }
19229         this.list.alignTo(this.el, this.listAlign);
19230         this.list.show();
19231         Roo.get(document).on('mousedown', this.collapseIf, this);
19232         Roo.get(document).on('mousewheel', this.collapseIf, this);
19233         if (!this.editable) {
19234             Roo.get(document).on('keydown', this.listKeyPress, this);
19235         }
19236         
19237         this.fireEvent('expand', this);
19238     },
19239
19240     // private
19241     // Implements the default empty TriggerField.onTriggerClick function
19242     onTriggerClick : function(){
19243         if(this.disabled){
19244             return;
19245         }
19246         if(this.isExpanded()){
19247             this.collapse();
19248             if (!this.blockFocus) {
19249                 this.el.focus();
19250             }
19251             
19252         }else {
19253             this.hasFocus = true;
19254             if(this.triggerAction == 'all') {
19255                 this.doQuery(this.allQuery, true);
19256             } else {
19257                 this.doQuery(this.getRawValue());
19258             }
19259             if (!this.blockFocus) {
19260                 this.el.focus();
19261             }
19262         }
19263     },
19264     listKeyPress : function(e)
19265     {
19266         //Roo.log('listkeypress');
19267         // scroll to first matching element based on key pres..
19268         if (e.isSpecialKey()) {
19269             return false;
19270         }
19271         var k = String.fromCharCode(e.getKey()).toUpperCase();
19272         //Roo.log(k);
19273         var match  = false;
19274         var csel = this.view.getSelectedNodes();
19275         var cselitem = false;
19276         if (csel.length) {
19277             var ix = this.view.indexOf(csel[0]);
19278             cselitem  = this.store.getAt(ix);
19279             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19280                 cselitem = false;
19281             }
19282             
19283         }
19284         
19285         this.store.each(function(v) { 
19286             if (cselitem) {
19287                 // start at existing selection.
19288                 if (cselitem.id == v.id) {
19289                     cselitem = false;
19290                 }
19291                 return;
19292             }
19293                 
19294             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19295                 match = this.store.indexOf(v);
19296                 return false;
19297             }
19298         }, this);
19299         
19300         if (match === false) {
19301             return true; // no more action?
19302         }
19303         // scroll to?
19304         this.view.select(match);
19305         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19306         sn.scrollIntoView(sn.dom.parentNode, false);
19307     } 
19308
19309     /** 
19310     * @cfg {Boolean} grow 
19311     * @hide 
19312     */
19313     /** 
19314     * @cfg {Number} growMin 
19315     * @hide 
19316     */
19317     /** 
19318     * @cfg {Number} growMax 
19319     * @hide 
19320     */
19321     /**
19322      * @hide
19323      * @method autoSize
19324      */
19325 });/*
19326  * Copyright(c) 2010-2012, Roo J Solutions Limited
19327  *
19328  * Licence LGPL
19329  *
19330  */
19331
19332 /**
19333  * @class Roo.form.ComboBoxArray
19334  * @extends Roo.form.TextField
19335  * A facebook style adder... for lists of email / people / countries  etc...
19336  * pick multiple items from a combo box, and shows each one.
19337  *
19338  *  Fred [x]  Brian [x]  [Pick another |v]
19339  *
19340  *
19341  *  For this to work: it needs various extra information
19342  *    - normal combo problay has
19343  *      name, hiddenName
19344  *    + displayField, valueField
19345  *
19346  *    For our purpose...
19347  *
19348  *
19349  *   If we change from 'extends' to wrapping...
19350  *   
19351  *  
19352  *
19353  
19354  
19355  * @constructor
19356  * Create a new ComboBoxArray.
19357  * @param {Object} config Configuration options
19358  */
19359  
19360
19361 Roo.form.ComboBoxArray = function(config)
19362 {
19363     this.addEvents({
19364         /**
19365          * @event beforeremove
19366          * Fires before remove the value from the list
19367              * @param {Roo.form.ComboBoxArray} _self This combo box array
19368              * @param {Roo.form.ComboBoxArray.Item} item removed item
19369              */
19370         'beforeremove' : true,
19371         /**
19372          * @event remove
19373          * Fires when remove the value from the list
19374              * @param {Roo.form.ComboBoxArray} _self This combo box array
19375              * @param {Roo.form.ComboBoxArray.Item} item removed item
19376              */
19377         'remove' : true
19378         
19379         
19380     });
19381     
19382     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19383     
19384     this.items = new Roo.util.MixedCollection(false);
19385     
19386     // construct the child combo...
19387     
19388     
19389     
19390     
19391    
19392     
19393 }
19394
19395  
19396 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19397
19398     /**
19399      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19400      */
19401     
19402     lastData : false,
19403     
19404     // behavies liek a hiddne field
19405     inputType:      'hidden',
19406     /**
19407      * @cfg {Number} width The width of the box that displays the selected element
19408      */ 
19409     width:          300,
19410
19411     
19412     
19413     /**
19414      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19415      */
19416     name : false,
19417     /**
19418      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19419      */
19420     hiddenName : false,
19421       /**
19422      * @cfg {String} seperator    The value seperator normally ',' 
19423      */
19424     seperator : ',',
19425     
19426     // private the array of items that are displayed..
19427     items  : false,
19428     // private - the hidden field el.
19429     hiddenEl : false,
19430     // private - the filed el..
19431     el : false,
19432     
19433     //validateValue : function() { return true; }, // all values are ok!
19434     //onAddClick: function() { },
19435     
19436     onRender : function(ct, position) 
19437     {
19438         
19439         // create the standard hidden element
19440         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19441         
19442         
19443         // give fake names to child combo;
19444         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19445         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19446         
19447         this.combo = Roo.factory(this.combo, Roo.form);
19448         this.combo.onRender(ct, position);
19449         if (typeof(this.combo.width) != 'undefined') {
19450             this.combo.onResize(this.combo.width,0);
19451         }
19452         
19453         this.combo.initEvents();
19454         
19455         // assigned so form know we need to do this..
19456         this.store          = this.combo.store;
19457         this.valueField     = this.combo.valueField;
19458         this.displayField   = this.combo.displayField ;
19459         
19460         
19461         this.combo.wrap.addClass('x-cbarray-grp');
19462         
19463         var cbwrap = this.combo.wrap.createChild(
19464             {tag: 'div', cls: 'x-cbarray-cb'},
19465             this.combo.el.dom
19466         );
19467         
19468              
19469         this.hiddenEl = this.combo.wrap.createChild({
19470             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19471         });
19472         this.el = this.combo.wrap.createChild({
19473             tag: 'input',  type:'hidden' , name: this.name, value : ''
19474         });
19475          //   this.el.dom.removeAttribute("name");
19476         
19477         
19478         this.outerWrap = this.combo.wrap;
19479         this.wrap = cbwrap;
19480         
19481         this.outerWrap.setWidth(this.width);
19482         this.outerWrap.dom.removeChild(this.el.dom);
19483         
19484         this.wrap.dom.appendChild(this.el.dom);
19485         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19486         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19487         
19488         this.combo.trigger.setStyle('position','relative');
19489         this.combo.trigger.setStyle('left', '0px');
19490         this.combo.trigger.setStyle('top', '2px');
19491         
19492         this.combo.el.setStyle('vertical-align', 'text-bottom');
19493         
19494         //this.trigger.setStyle('vertical-align', 'top');
19495         
19496         // this should use the code from combo really... on('add' ....)
19497         if (this.adder) {
19498             
19499         
19500             this.adder = this.outerWrap.createChild(
19501                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19502             var _t = this;
19503             this.adder.on('click', function(e) {
19504                 _t.fireEvent('adderclick', this, e);
19505             }, _t);
19506         }
19507         //var _t = this;
19508         //this.adder.on('click', this.onAddClick, _t);
19509         
19510         
19511         this.combo.on('select', function(cb, rec, ix) {
19512             this.addItem(rec.data);
19513             
19514             cb.setValue('');
19515             cb.el.dom.value = '';
19516             //cb.lastData = rec.data;
19517             // add to list
19518             
19519         }, this);
19520         
19521         
19522     },
19523     
19524     
19525     getName: function()
19526     {
19527         // returns hidden if it's set..
19528         if (!this.rendered) {return ''};
19529         return  this.hiddenName ? this.hiddenName : this.name;
19530         
19531     },
19532     
19533     
19534     onResize: function(w, h){
19535         
19536         return;
19537         // not sure if this is needed..
19538         //this.combo.onResize(w,h);
19539         
19540         if(typeof w != 'number'){
19541             // we do not handle it!?!?
19542             return;
19543         }
19544         var tw = this.combo.trigger.getWidth();
19545         tw += this.addicon ? this.addicon.getWidth() : 0;
19546         tw += this.editicon ? this.editicon.getWidth() : 0;
19547         var x = w - tw;
19548         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19549             
19550         this.combo.trigger.setStyle('left', '0px');
19551         
19552         if(this.list && this.listWidth === undefined){
19553             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19554             this.list.setWidth(lw);
19555             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19556         }
19557         
19558     
19559         
19560     },
19561     
19562     addItem: function(rec)
19563     {
19564         var valueField = this.combo.valueField;
19565         var displayField = this.combo.displayField;
19566         
19567         if (this.items.indexOfKey(rec[valueField]) > -1) {
19568             //console.log("GOT " + rec.data.id);
19569             return;
19570         }
19571         
19572         var x = new Roo.form.ComboBoxArray.Item({
19573             //id : rec[this.idField],
19574             data : rec,
19575             displayField : displayField ,
19576             tipField : displayField ,
19577             cb : this
19578         });
19579         // use the 
19580         this.items.add(rec[valueField],x);
19581         // add it before the element..
19582         this.updateHiddenEl();
19583         x.render(this.outerWrap, this.wrap.dom);
19584         // add the image handler..
19585     },
19586     
19587     updateHiddenEl : function()
19588     {
19589         this.validate();
19590         if (!this.hiddenEl) {
19591             return;
19592         }
19593         var ar = [];
19594         var idField = this.combo.valueField;
19595         
19596         this.items.each(function(f) {
19597             ar.push(f.data[idField]);
19598         });
19599         this.hiddenEl.dom.value = ar.join(this.seperator);
19600         this.validate();
19601     },
19602     
19603     reset : function()
19604     {
19605         this.items.clear();
19606         
19607         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19608            el.remove();
19609         });
19610         
19611         this.el.dom.value = '';
19612         if (this.hiddenEl) {
19613             this.hiddenEl.dom.value = '';
19614         }
19615         
19616     },
19617     getValue: function()
19618     {
19619         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19620     },
19621     setValue: function(v) // not a valid action - must use addItems..
19622     {
19623         
19624         this.reset();
19625          
19626         if (this.store.isLocal && (typeof(v) == 'string')) {
19627             // then we can use the store to find the values..
19628             // comma seperated at present.. this needs to allow JSON based encoding..
19629             this.hiddenEl.value  = v;
19630             var v_ar = [];
19631             Roo.each(v.split(this.seperator), function(k) {
19632                 Roo.log("CHECK " + this.valueField + ',' + k);
19633                 var li = this.store.query(this.valueField, k);
19634                 if (!li.length) {
19635                     return;
19636                 }
19637                 var add = {};
19638                 add[this.valueField] = k;
19639                 add[this.displayField] = li.item(0).data[this.displayField];
19640                 
19641                 this.addItem(add);
19642             }, this) 
19643              
19644         }
19645         if (typeof(v) == 'object' ) {
19646             // then let's assume it's an array of objects..
19647             Roo.each(v, function(l) {
19648                 var add = l;
19649                 if (typeof(l) == 'string') {
19650                     add = {};
19651                     add[this.valueField] = l;
19652                     add[this.displayField] = l
19653                 }
19654                 this.addItem(add);
19655             }, this);
19656              
19657         }
19658         
19659         
19660     },
19661     setFromData: function(v)
19662     {
19663         // this recieves an object, if setValues is called.
19664         this.reset();
19665         this.el.dom.value = v[this.displayField];
19666         this.hiddenEl.dom.value = v[this.valueField];
19667         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19668             return;
19669         }
19670         var kv = v[this.valueField];
19671         var dv = v[this.displayField];
19672         kv = typeof(kv) != 'string' ? '' : kv;
19673         dv = typeof(dv) != 'string' ? '' : dv;
19674         
19675         
19676         var keys = kv.split(this.seperator);
19677         var display = dv.split(this.seperator);
19678         for (var i = 0 ; i < keys.length; i++) {
19679             add = {};
19680             add[this.valueField] = keys[i];
19681             add[this.displayField] = display[i];
19682             this.addItem(add);
19683         }
19684       
19685         
19686     },
19687     
19688     /**
19689      * Validates the combox array value
19690      * @return {Boolean} True if the value is valid, else false
19691      */
19692     validate : function(){
19693         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19694             this.clearInvalid();
19695             return true;
19696         }
19697         return false;
19698     },
19699     
19700     validateValue : function(value){
19701         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19702         
19703     },
19704     
19705     /*@
19706      * overide
19707      * 
19708      */
19709     isDirty : function() {
19710         if(this.disabled) {
19711             return false;
19712         }
19713         
19714         try {
19715             var d = Roo.decode(String(this.originalValue));
19716         } catch (e) {
19717             return String(this.getValue()) !== String(this.originalValue);
19718         }
19719         
19720         var originalValue = [];
19721         
19722         for (var i = 0; i < d.length; i++){
19723             originalValue.push(d[i][this.valueField]);
19724         }
19725         
19726         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19727         
19728     }
19729     
19730 });
19731
19732
19733
19734 /**
19735  * @class Roo.form.ComboBoxArray.Item
19736  * @extends Roo.BoxComponent
19737  * A selected item in the list
19738  *  Fred [x]  Brian [x]  [Pick another |v]
19739  * 
19740  * @constructor
19741  * Create a new item.
19742  * @param {Object} config Configuration options
19743  */
19744  
19745 Roo.form.ComboBoxArray.Item = function(config) {
19746     config.id = Roo.id();
19747     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19748 }
19749
19750 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19751     data : {},
19752     cb: false,
19753     displayField : false,
19754     tipField : false,
19755     
19756     
19757     defaultAutoCreate : {
19758         tag: 'div',
19759         cls: 'x-cbarray-item',
19760         cn : [ 
19761             { tag: 'div' },
19762             {
19763                 tag: 'img',
19764                 width:16,
19765                 height : 16,
19766                 src : Roo.BLANK_IMAGE_URL ,
19767                 align: 'center'
19768             }
19769         ]
19770         
19771     },
19772     
19773  
19774     onRender : function(ct, position)
19775     {
19776         Roo.form.Field.superclass.onRender.call(this, ct, position);
19777         
19778         if(!this.el){
19779             var cfg = this.getAutoCreate();
19780             this.el = ct.createChild(cfg, position);
19781         }
19782         
19783         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19784         
19785         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19786             this.cb.renderer(this.data) :
19787             String.format('{0}',this.data[this.displayField]);
19788         
19789             
19790         this.el.child('div').dom.setAttribute('qtip',
19791                         String.format('{0}',this.data[this.tipField])
19792         );
19793         
19794         this.el.child('img').on('click', this.remove, this);
19795         
19796     },
19797    
19798     remove : function()
19799     {
19800         if(this.cb.disabled){
19801             return;
19802         }
19803         
19804         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19805             this.cb.items.remove(this);
19806             this.el.child('img').un('click', this.remove, this);
19807             this.el.remove();
19808             this.cb.updateHiddenEl();
19809
19810             this.cb.fireEvent('remove', this.cb, this);
19811         }
19812         
19813     }
19814 });/*
19815  * RooJS Library 1.1.1
19816  * Copyright(c) 2008-2011  Alan Knowles
19817  *
19818  * License - LGPL
19819  */
19820  
19821
19822 /**
19823  * @class Roo.form.ComboNested
19824  * @extends Roo.form.ComboBox
19825  * A combobox for that allows selection of nested items in a list,
19826  * eg.
19827  *
19828  *  Book
19829  *    -> red
19830  *    -> green
19831  *  Table
19832  *    -> square
19833  *      ->red
19834  *      ->green
19835  *    -> rectangle
19836  *      ->green
19837  *      
19838  * 
19839  * @constructor
19840  * Create a new ComboNested
19841  * @param {Object} config Configuration options
19842  */
19843 Roo.form.ComboNested = function(config){
19844     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19845     // should verify some data...
19846     // like
19847     // hiddenName = required..
19848     // displayField = required
19849     // valudField == required
19850     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19851     var _t = this;
19852     Roo.each(req, function(e) {
19853         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19854             throw "Roo.form.ComboNested : missing value for: " + e;
19855         }
19856     });
19857      
19858     
19859 };
19860
19861 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19862    
19863     /*
19864      * @config {Number} max Number of columns to show
19865      */
19866     
19867     maxColumns : 3,
19868    
19869     list : null, // the outermost div..
19870     innerLists : null, // the
19871     views : null,
19872     stores : null,
19873     // private
19874     loadingChildren : false,
19875     
19876     onRender : function(ct, position)
19877     {
19878         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19879         
19880         if(this.hiddenName){
19881             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19882                     'before', true);
19883             this.hiddenField.value =
19884                 this.hiddenValue !== undefined ? this.hiddenValue :
19885                 this.value !== undefined ? this.value : '';
19886
19887             // prevent input submission
19888             this.el.dom.removeAttribute('name');
19889              
19890              
19891         }
19892         
19893         if(Roo.isGecko){
19894             this.el.dom.setAttribute('autocomplete', 'off');
19895         }
19896
19897         var cls = 'x-combo-list';
19898
19899         this.list = new Roo.Layer({
19900             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19901         });
19902
19903         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19904         this.list.setWidth(lw);
19905         this.list.swallowEvent('mousewheel');
19906         this.assetHeight = 0;
19907
19908         if(this.title){
19909             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19910             this.assetHeight += this.header.getHeight();
19911         }
19912         this.innerLists = [];
19913         this.views = [];
19914         this.stores = [];
19915         for (var i =0 ; i < this.maxColumns; i++) {
19916             this.onRenderList( cls, i);
19917         }
19918         
19919         // always needs footer, as we are going to have an 'OK' button.
19920         this.footer = this.list.createChild({cls:cls+'-ft'});
19921         this.pageTb = new Roo.Toolbar(this.footer);  
19922         var _this = this;
19923         this.pageTb.add(  {
19924             
19925             text: 'Done',
19926             handler: function()
19927             {
19928                 _this.collapse();
19929             }
19930         });
19931         
19932         if ( this.allowBlank && !this.disableClear) {
19933             
19934             this.pageTb.add(new Roo.Toolbar.Fill(), {
19935                 cls: 'x-btn-icon x-btn-clear',
19936                 text: '&#160;',
19937                 handler: function()
19938                 {
19939                     _this.collapse();
19940                     _this.clearValue();
19941                     _this.onSelect(false, -1);
19942                 }
19943             });
19944         }
19945         if (this.footer) {
19946             this.assetHeight += this.footer.getHeight();
19947         }
19948         
19949     },
19950     onRenderList : function (  cls, i)
19951     {
19952         
19953         var lw = Math.floor(
19954                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19955         );
19956         
19957         this.list.setWidth(lw); // default to '1'
19958
19959         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19960         //il.on('mouseover', this.onViewOver, this, { list:  i });
19961         //il.on('mousemove', this.onViewMove, this, { list:  i });
19962         il.setWidth(lw);
19963         il.setStyle({ 'overflow-x' : 'hidden'});
19964
19965         if(!this.tpl){
19966             this.tpl = new Roo.Template({
19967                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19968                 isEmpty: function (value, allValues) {
19969                     //Roo.log(value);
19970                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19971                     return dl ? 'has-children' : 'no-children'
19972                 }
19973             });
19974         }
19975         
19976         var store  = this.store;
19977         if (i > 0) {
19978             store  = new Roo.data.SimpleStore({
19979                 //fields : this.store.reader.meta.fields,
19980                 reader : this.store.reader,
19981                 data : [ ]
19982             });
19983         }
19984         this.stores[i]  = store;
19985                   
19986         var view = this.views[i] = new Roo.View(
19987             il,
19988             this.tpl,
19989             {
19990                 singleSelect:true,
19991                 store: store,
19992                 selectedClass: this.selectedClass
19993             }
19994         );
19995         view.getEl().setWidth(lw);
19996         view.getEl().setStyle({
19997             position: i < 1 ? 'relative' : 'absolute',
19998             top: 0,
19999             left: (i * lw ) + 'px',
20000             display : i > 0 ? 'none' : 'block'
20001         });
20002         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20003         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20004         //view.on('click', this.onViewClick, this, { list : i });
20005
20006         store.on('beforeload', this.onBeforeLoad, this);
20007         store.on('load',  this.onLoad, this, { list  : i});
20008         store.on('loadexception', this.onLoadException, this);
20009
20010         // hide the other vies..
20011         
20012         
20013         
20014     },
20015       
20016     restrictHeight : function()
20017     {
20018         var mh = 0;
20019         Roo.each(this.innerLists, function(il,i) {
20020             var el = this.views[i].getEl();
20021             el.dom.style.height = '';
20022             var inner = el.dom;
20023             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20024             // only adjust heights on other ones..
20025             mh = Math.max(h, mh);
20026             if (i < 1) {
20027                 
20028                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20029                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20030                
20031             }
20032             
20033             
20034         }, this);
20035         
20036         this.list.beginUpdate();
20037         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20038         this.list.alignTo(this.el, this.listAlign);
20039         this.list.endUpdate();
20040         
20041     },
20042      
20043     
20044     // -- store handlers..
20045     // private
20046     onBeforeLoad : function()
20047     {
20048         if(!this.hasFocus){
20049             return;
20050         }
20051         this.innerLists[0].update(this.loadingText ?
20052                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20053         this.restrictHeight();
20054         this.selectedIndex = -1;
20055     },
20056     // private
20057     onLoad : function(a,b,c,d)
20058     {
20059         if (!this.loadingChildren) {
20060             // then we are loading the top level. - hide the children
20061             for (var i = 1;i < this.views.length; i++) {
20062                 this.views[i].getEl().setStyle({ display : 'none' });
20063             }
20064             var lw = Math.floor(
20065                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20066             );
20067         
20068              this.list.setWidth(lw); // default to '1'
20069
20070             
20071         }
20072         if(!this.hasFocus){
20073             return;
20074         }
20075         
20076         if(this.store.getCount() > 0) {
20077             this.expand();
20078             this.restrictHeight();   
20079         } else {
20080             this.onEmptyResults();
20081         }
20082         
20083         if (!this.loadingChildren) {
20084             this.selectActive();
20085         }
20086         /*
20087         this.stores[1].loadData([]);
20088         this.stores[2].loadData([]);
20089         this.views
20090         */    
20091     
20092         //this.el.focus();
20093     },
20094     
20095     
20096     // private
20097     onLoadException : function()
20098     {
20099         this.collapse();
20100         Roo.log(this.store.reader.jsonData);
20101         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20102             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20103         }
20104         
20105         
20106     },
20107     // no cleaning of leading spaces on blur here.
20108     cleanLeadingSpace : function(e) { },
20109     
20110
20111     onSelectChange : function (view, sels, opts )
20112     {
20113         var ix = view.getSelectedIndexes();
20114          
20115         if (opts.list > this.maxColumns - 2) {
20116             if (view.store.getCount()<  1) {
20117                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20118
20119             } else  {
20120                 if (ix.length) {
20121                     // used to clear ?? but if we are loading unselected 
20122                     this.setFromData(view.store.getAt(ix[0]).data);
20123                 }
20124                 
20125             }
20126             
20127             return;
20128         }
20129         
20130         if (!ix.length) {
20131             // this get's fired when trigger opens..
20132            // this.setFromData({});
20133             var str = this.stores[opts.list+1];
20134             str.data.clear(); // removeall wihtout the fire events..
20135             return;
20136         }
20137         
20138         var rec = view.store.getAt(ix[0]);
20139          
20140         this.setFromData(rec.data);
20141         this.fireEvent('select', this, rec, ix[0]);
20142         
20143         var lw = Math.floor(
20144              (
20145                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20146              ) / this.maxColumns
20147         );
20148         this.loadingChildren = true;
20149         this.stores[opts.list+1].loadDataFromChildren( rec );
20150         this.loadingChildren = false;
20151         var dl = this.stores[opts.list+1]. getTotalCount();
20152         
20153         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20154         
20155         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20156         for (var i = opts.list+2; i < this.views.length;i++) {
20157             this.views[i].getEl().setStyle({ display : 'none' });
20158         }
20159         
20160         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20161         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20162         
20163         if (this.isLoading) {
20164            // this.selectActive(opts.list);
20165         }
20166          
20167     },
20168     
20169     
20170     
20171     
20172     onDoubleClick : function()
20173     {
20174         this.collapse(); //??
20175     },
20176     
20177      
20178     
20179     
20180     
20181     // private
20182     recordToStack : function(store, prop, value, stack)
20183     {
20184         var cstore = new Roo.data.SimpleStore({
20185             //fields : this.store.reader.meta.fields, // we need array reader.. for
20186             reader : this.store.reader,
20187             data : [ ]
20188         });
20189         var _this = this;
20190         var record  = false;
20191         var srec = false;
20192         if(store.getCount() < 1){
20193             return false;
20194         }
20195         store.each(function(r){
20196             if(r.data[prop] == value){
20197                 record = r;
20198             srec = r;
20199                 return false;
20200             }
20201             if (r.data.cn && r.data.cn.length) {
20202                 cstore.loadDataFromChildren( r);
20203                 var cret = _this.recordToStack(cstore, prop, value, stack);
20204                 if (cret !== false) {
20205                     record = cret;
20206                     srec = r;
20207                     return false;
20208                 }
20209             }
20210              
20211             return true;
20212         });
20213         if (record == false) {
20214             return false
20215         }
20216         stack.unshift(srec);
20217         return record;
20218     },
20219     
20220     /*
20221      * find the stack of stores that match our value.
20222      *
20223      * 
20224      */
20225     
20226     selectActive : function ()
20227     {
20228         // if store is not loaded, then we will need to wait for that to happen first.
20229         var stack = [];
20230         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20231         for (var i = 0; i < stack.length; i++ ) {
20232             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20233         }
20234         
20235     }
20236         
20237          
20238     
20239     
20240     
20241     
20242 });/*
20243  * Based on:
20244  * Ext JS Library 1.1.1
20245  * Copyright(c) 2006-2007, Ext JS, LLC.
20246  *
20247  * Originally Released Under LGPL - original licence link has changed is not relivant.
20248  *
20249  * Fork - LGPL
20250  * <script type="text/javascript">
20251  */
20252 /**
20253  * @class Roo.form.Checkbox
20254  * @extends Roo.form.Field
20255  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20256  * @constructor
20257  * Creates a new Checkbox
20258  * @param {Object} config Configuration options
20259  */
20260 Roo.form.Checkbox = function(config){
20261     Roo.form.Checkbox.superclass.constructor.call(this, config);
20262     this.addEvents({
20263         /**
20264          * @event check
20265          * Fires when the checkbox is checked or unchecked.
20266              * @param {Roo.form.Checkbox} this This checkbox
20267              * @param {Boolean} checked The new checked value
20268              */
20269         check : true
20270     });
20271 };
20272
20273 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20274     /**
20275      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20276      */
20277     focusClass : undefined,
20278     /**
20279      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20280      */
20281     fieldClass: "x-form-field",
20282     /**
20283      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20284      */
20285     checked: false,
20286     /**
20287      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20288      * {tag: "input", type: "checkbox", autocomplete: "off"})
20289      */
20290     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20291     /**
20292      * @cfg {String} boxLabel The text that appears beside the checkbox
20293      */
20294     boxLabel : "",
20295     /**
20296      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20297      */  
20298     inputValue : '1',
20299     /**
20300      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20301      */
20302      valueOff: '0', // value when not checked..
20303
20304     actionMode : 'viewEl', 
20305     //
20306     // private
20307     itemCls : 'x-menu-check-item x-form-item',
20308     groupClass : 'x-menu-group-item',
20309     inputType : 'hidden',
20310     
20311     
20312     inSetChecked: false, // check that we are not calling self...
20313     
20314     inputElement: false, // real input element?
20315     basedOn: false, // ????
20316     
20317     isFormField: true, // not sure where this is needed!!!!
20318
20319     onResize : function(){
20320         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20321         if(!this.boxLabel){
20322             this.el.alignTo(this.wrap, 'c-c');
20323         }
20324     },
20325
20326     initEvents : function(){
20327         Roo.form.Checkbox.superclass.initEvents.call(this);
20328         this.el.on("click", this.onClick,  this);
20329         this.el.on("change", this.onClick,  this);
20330     },
20331
20332
20333     getResizeEl : function(){
20334         return this.wrap;
20335     },
20336
20337     getPositionEl : function(){
20338         return this.wrap;
20339     },
20340
20341     // private
20342     onRender : function(ct, position){
20343         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20344         /*
20345         if(this.inputValue !== undefined){
20346             this.el.dom.value = this.inputValue;
20347         }
20348         */
20349         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20350         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20351         var viewEl = this.wrap.createChild({ 
20352             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20353         this.viewEl = viewEl;   
20354         this.wrap.on('click', this.onClick,  this); 
20355         
20356         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20357         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20358         
20359         
20360         
20361         if(this.boxLabel){
20362             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20363         //    viewEl.on('click', this.onClick,  this); 
20364         }
20365         //if(this.checked){
20366             this.setChecked(this.checked);
20367         //}else{
20368             //this.checked = this.el.dom;
20369         //}
20370
20371     },
20372
20373     // private
20374     initValue : Roo.emptyFn,
20375
20376     /**
20377      * Returns the checked state of the checkbox.
20378      * @return {Boolean} True if checked, else false
20379      */
20380     getValue : function(){
20381         if(this.el){
20382             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20383         }
20384         return this.valueOff;
20385         
20386     },
20387
20388         // private
20389     onClick : function(){ 
20390         if (this.disabled) {
20391             return;
20392         }
20393         this.setChecked(!this.checked);
20394
20395         //if(this.el.dom.checked != this.checked){
20396         //    this.setValue(this.el.dom.checked);
20397        // }
20398     },
20399
20400     /**
20401      * Sets the checked state of the checkbox.
20402      * On is always based on a string comparison between inputValue and the param.
20403      * @param {Boolean/String} value - the value to set 
20404      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20405      */
20406     setValue : function(v,suppressEvent){
20407         
20408         
20409         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20410         //if(this.el && this.el.dom){
20411         //    this.el.dom.checked = this.checked;
20412         //    this.el.dom.defaultChecked = this.checked;
20413         //}
20414         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20415         //this.fireEvent("check", this, this.checked);
20416     },
20417     // private..
20418     setChecked : function(state,suppressEvent)
20419     {
20420         if (this.inSetChecked) {
20421             this.checked = state;
20422             return;
20423         }
20424         
20425     
20426         if(this.wrap){
20427             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20428         }
20429         this.checked = state;
20430         if(suppressEvent !== true){
20431             this.fireEvent('check', this, state);
20432         }
20433         this.inSetChecked = true;
20434         this.el.dom.value = state ? this.inputValue : this.valueOff;
20435         this.inSetChecked = false;
20436         
20437     },
20438     // handle setting of hidden value by some other method!!?!?
20439     setFromHidden: function()
20440     {
20441         if(!this.el){
20442             return;
20443         }
20444         //console.log("SET FROM HIDDEN");
20445         //alert('setFrom hidden');
20446         this.setValue(this.el.dom.value);
20447     },
20448     
20449     onDestroy : function()
20450     {
20451         if(this.viewEl){
20452             Roo.get(this.viewEl).remove();
20453         }
20454          
20455         Roo.form.Checkbox.superclass.onDestroy.call(this);
20456     },
20457     
20458     setBoxLabel : function(str)
20459     {
20460         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20461     }
20462
20463 });/*
20464  * Based on:
20465  * Ext JS Library 1.1.1
20466  * Copyright(c) 2006-2007, Ext JS, LLC.
20467  *
20468  * Originally Released Under LGPL - original licence link has changed is not relivant.
20469  *
20470  * Fork - LGPL
20471  * <script type="text/javascript">
20472  */
20473  
20474 /**
20475  * @class Roo.form.Radio
20476  * @extends Roo.form.Checkbox
20477  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20478  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20479  * @constructor
20480  * Creates a new Radio
20481  * @param {Object} config Configuration options
20482  */
20483 Roo.form.Radio = function(){
20484     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20485 };
20486 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20487     inputType: 'radio',
20488
20489     /**
20490      * If this radio is part of a group, it will return the selected value
20491      * @return {String}
20492      */
20493     getGroupValue : function(){
20494         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20495     },
20496     
20497     
20498     onRender : function(ct, position){
20499         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20500         
20501         if(this.inputValue !== undefined){
20502             this.el.dom.value = this.inputValue;
20503         }
20504          
20505         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20506         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20507         //var viewEl = this.wrap.createChild({ 
20508         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20509         //this.viewEl = viewEl;   
20510         //this.wrap.on('click', this.onClick,  this); 
20511         
20512         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20513         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20514         
20515         
20516         
20517         if(this.boxLabel){
20518             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20519         //    viewEl.on('click', this.onClick,  this); 
20520         }
20521          if(this.checked){
20522             this.el.dom.checked =   'checked' ;
20523         }
20524          
20525     } 
20526     
20527     
20528 });//<script type="text/javascript">
20529
20530 /*
20531  * Based  Ext JS Library 1.1.1
20532  * Copyright(c) 2006-2007, Ext JS, LLC.
20533  * LGPL
20534  *
20535  */
20536  
20537 /**
20538  * @class Roo.HtmlEditorCore
20539  * @extends Roo.Component
20540  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20541  *
20542  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20543  */
20544
20545 Roo.HtmlEditorCore = function(config){
20546     
20547     
20548     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20549     
20550     
20551     this.addEvents({
20552         /**
20553          * @event initialize
20554          * Fires when the editor is fully initialized (including the iframe)
20555          * @param {Roo.HtmlEditorCore} this
20556          */
20557         initialize: true,
20558         /**
20559          * @event activate
20560          * Fires when the editor is first receives the focus. Any insertion must wait
20561          * until after this event.
20562          * @param {Roo.HtmlEditorCore} this
20563          */
20564         activate: true,
20565          /**
20566          * @event beforesync
20567          * Fires before the textarea is updated with content from the editor iframe. Return false
20568          * to cancel the sync.
20569          * @param {Roo.HtmlEditorCore} this
20570          * @param {String} html
20571          */
20572         beforesync: true,
20573          /**
20574          * @event beforepush
20575          * Fires before the iframe editor is updated with content from the textarea. Return false
20576          * to cancel the push.
20577          * @param {Roo.HtmlEditorCore} this
20578          * @param {String} html
20579          */
20580         beforepush: true,
20581          /**
20582          * @event sync
20583          * Fires when the textarea is updated with content from the editor iframe.
20584          * @param {Roo.HtmlEditorCore} this
20585          * @param {String} html
20586          */
20587         sync: true,
20588          /**
20589          * @event push
20590          * Fires when the iframe editor is updated with content from the textarea.
20591          * @param {Roo.HtmlEditorCore} this
20592          * @param {String} html
20593          */
20594         push: true,
20595         
20596         /**
20597          * @event editorevent
20598          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20599          * @param {Roo.HtmlEditorCore} this
20600          */
20601         editorevent: true
20602         
20603     });
20604     
20605     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20606     
20607     // defaults : white / black...
20608     this.applyBlacklists();
20609     
20610     
20611     
20612 };
20613
20614
20615 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20616
20617
20618      /**
20619      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20620      */
20621     
20622     owner : false,
20623     
20624      /**
20625      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20626      *                        Roo.resizable.
20627      */
20628     resizable : false,
20629      /**
20630      * @cfg {Number} height (in pixels)
20631      */   
20632     height: 300,
20633    /**
20634      * @cfg {Number} width (in pixels)
20635      */   
20636     width: 500,
20637     
20638     /**
20639      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20640      * 
20641      */
20642     stylesheets: false,
20643     
20644     /**
20645      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20646      */
20647     allowComments: false,
20648     // id of frame..
20649     frameId: false,
20650     
20651     // private properties
20652     validationEvent : false,
20653     deferHeight: true,
20654     initialized : false,
20655     activated : false,
20656     sourceEditMode : false,
20657     onFocus : Roo.emptyFn,
20658     iframePad:3,
20659     hideMode:'offsets',
20660     
20661     clearUp: true,
20662     
20663     // blacklist + whitelisted elements..
20664     black: false,
20665     white: false,
20666      
20667     bodyCls : '',
20668
20669     /**
20670      * Protected method that will not generally be called directly. It
20671      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20672      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20673      */
20674     getDocMarkup : function(){
20675         // body styles..
20676         var st = '';
20677         
20678         // inherit styels from page...?? 
20679         if (this.stylesheets === false) {
20680             
20681             Roo.get(document.head).select('style').each(function(node) {
20682                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20683             });
20684             
20685             Roo.get(document.head).select('link').each(function(node) { 
20686                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20687             });
20688             
20689         } else if (!this.stylesheets.length) {
20690                 // simple..
20691                 st = '<style type="text/css">' +
20692                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20693                    '</style>';
20694         } else {
20695             for (var i in this.stylesheets) { 
20696                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20697             }
20698             
20699         }
20700         
20701         st +=  '<style type="text/css">' +
20702             'IMG { cursor: pointer } ' +
20703         '</style>';
20704
20705         var cls = 'roo-htmleditor-body';
20706         
20707         if(this.bodyCls.length){
20708             cls += ' ' + this.bodyCls;
20709         }
20710         
20711         return '<html><head>' + st  +
20712             //<style type="text/css">' +
20713             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20714             //'</style>' +
20715             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20716     },
20717
20718     // private
20719     onRender : function(ct, position)
20720     {
20721         var _t = this;
20722         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20723         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20724         
20725         
20726         this.el.dom.style.border = '0 none';
20727         this.el.dom.setAttribute('tabIndex', -1);
20728         this.el.addClass('x-hidden hide');
20729         
20730         
20731         
20732         if(Roo.isIE){ // fix IE 1px bogus margin
20733             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20734         }
20735        
20736         
20737         this.frameId = Roo.id();
20738         
20739          
20740         
20741         var iframe = this.owner.wrap.createChild({
20742             tag: 'iframe',
20743             cls: 'form-control', // bootstrap..
20744             id: this.frameId,
20745             name: this.frameId,
20746             frameBorder : 'no',
20747             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20748         }, this.el
20749         );
20750         
20751         
20752         this.iframe = iframe.dom;
20753
20754          this.assignDocWin();
20755         
20756         this.doc.designMode = 'on';
20757        
20758         this.doc.open();
20759         this.doc.write(this.getDocMarkup());
20760         this.doc.close();
20761
20762         
20763         var task = { // must defer to wait for browser to be ready
20764             run : function(){
20765                 //console.log("run task?" + this.doc.readyState);
20766                 this.assignDocWin();
20767                 if(this.doc.body || this.doc.readyState == 'complete'){
20768                     try {
20769                         this.doc.designMode="on";
20770                     } catch (e) {
20771                         return;
20772                     }
20773                     Roo.TaskMgr.stop(task);
20774                     this.initEditor.defer(10, this);
20775                 }
20776             },
20777             interval : 10,
20778             duration: 10000,
20779             scope: this
20780         };
20781         Roo.TaskMgr.start(task);
20782
20783     },
20784
20785     // private
20786     onResize : function(w, h)
20787     {
20788          Roo.log('resize: ' +w + ',' + h );
20789         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20790         if(!this.iframe){
20791             return;
20792         }
20793         if(typeof w == 'number'){
20794             
20795             this.iframe.style.width = w + 'px';
20796         }
20797         if(typeof h == 'number'){
20798             
20799             this.iframe.style.height = h + 'px';
20800             if(this.doc){
20801                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20802             }
20803         }
20804         
20805     },
20806
20807     /**
20808      * Toggles the editor between standard and source edit mode.
20809      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20810      */
20811     toggleSourceEdit : function(sourceEditMode){
20812         
20813         this.sourceEditMode = sourceEditMode === true;
20814         
20815         if(this.sourceEditMode){
20816  
20817             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20818             
20819         }else{
20820             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20821             //this.iframe.className = '';
20822             this.deferFocus();
20823         }
20824         //this.setSize(this.owner.wrap.getSize());
20825         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20826     },
20827
20828     
20829   
20830
20831     /**
20832      * Protected method that will not generally be called directly. If you need/want
20833      * custom HTML cleanup, this is the method you should override.
20834      * @param {String} html The HTML to be cleaned
20835      * return {String} The cleaned HTML
20836      */
20837     cleanHtml : function(html){
20838         html = String(html);
20839         if(html.length > 5){
20840             if(Roo.isSafari){ // strip safari nonsense
20841                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20842             }
20843         }
20844         if(html == '&nbsp;'){
20845             html = '';
20846         }
20847         return html;
20848     },
20849
20850     /**
20851      * HTML Editor -> Textarea
20852      * Protected method that will not generally be called directly. Syncs the contents
20853      * of the editor iframe with the textarea.
20854      */
20855     syncValue : function(){
20856         if(this.initialized){
20857             var bd = (this.doc.body || this.doc.documentElement);
20858             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20859             var html = bd.innerHTML;
20860             if(Roo.isSafari){
20861                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20862                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20863                 if(m && m[1]){
20864                     html = '<div style="'+m[0]+'">' + html + '</div>';
20865                 }
20866             }
20867             html = this.cleanHtml(html);
20868             // fix up the special chars.. normaly like back quotes in word...
20869             // however we do not want to do this with chinese..
20870             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20871                 
20872                 var cc = match.charCodeAt();
20873
20874                 // Get the character value, handling surrogate pairs
20875                 if (match.length == 2) {
20876                     // It's a surrogate pair, calculate the Unicode code point
20877                     var high = match.charCodeAt(0) - 0xD800;
20878                     var low  = match.charCodeAt(1) - 0xDC00;
20879                     cc = (high * 0x400) + low + 0x10000;
20880                 }  else if (
20881                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20882                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20883                     (cc >= 0xf900 && cc < 0xfb00 )
20884                 ) {
20885                         return match;
20886                 }  
20887          
20888                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20889                 return "&#" + cc + ";";
20890                 
20891                 
20892             });
20893             
20894             
20895              
20896             if(this.owner.fireEvent('beforesync', this, html) !== false){
20897                 this.el.dom.value = html;
20898                 this.owner.fireEvent('sync', this, html);
20899             }
20900         }
20901     },
20902
20903     /**
20904      * Protected method that will not generally be called directly. Pushes the value of the textarea
20905      * into the iframe editor.
20906      */
20907     pushValue : function(){
20908         if(this.initialized){
20909             var v = this.el.dom.value.trim();
20910             
20911 //            if(v.length < 1){
20912 //                v = '&#160;';
20913 //            }
20914             
20915             if(this.owner.fireEvent('beforepush', this, v) !== false){
20916                 var d = (this.doc.body || this.doc.documentElement);
20917                 d.innerHTML = v;
20918                 this.cleanUpPaste();
20919                 this.el.dom.value = d.innerHTML;
20920                 this.owner.fireEvent('push', this, v);
20921             }
20922         }
20923     },
20924
20925     // private
20926     deferFocus : function(){
20927         this.focus.defer(10, this);
20928     },
20929
20930     // doc'ed in Field
20931     focus : function(){
20932         if(this.win && !this.sourceEditMode){
20933             this.win.focus();
20934         }else{
20935             this.el.focus();
20936         }
20937     },
20938     
20939     assignDocWin: function()
20940     {
20941         var iframe = this.iframe;
20942         
20943          if(Roo.isIE){
20944             this.doc = iframe.contentWindow.document;
20945             this.win = iframe.contentWindow;
20946         } else {
20947 //            if (!Roo.get(this.frameId)) {
20948 //                return;
20949 //            }
20950 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20951 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20952             
20953             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20954                 return;
20955             }
20956             
20957             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20958             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20959         }
20960     },
20961     
20962     // private
20963     initEditor : function(){
20964         //console.log("INIT EDITOR");
20965         this.assignDocWin();
20966         
20967         
20968         
20969         this.doc.designMode="on";
20970         this.doc.open();
20971         this.doc.write(this.getDocMarkup());
20972         this.doc.close();
20973         
20974         var dbody = (this.doc.body || this.doc.documentElement);
20975         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20976         // this copies styles from the containing element into thsi one..
20977         // not sure why we need all of this..
20978         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20979         
20980         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20981         //ss['background-attachment'] = 'fixed'; // w3c
20982         dbody.bgProperties = 'fixed'; // ie
20983         //Roo.DomHelper.applyStyles(dbody, ss);
20984         Roo.EventManager.on(this.doc, {
20985             //'mousedown': this.onEditorEvent,
20986             'mouseup': this.onEditorEvent,
20987             'dblclick': this.onEditorEvent,
20988             'click': this.onEditorEvent,
20989             'keyup': this.onEditorEvent,
20990             buffer:100,
20991             scope: this
20992         });
20993         if(Roo.isGecko){
20994             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20995         }
20996         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20997             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20998         }
20999         this.initialized = true;
21000
21001         this.owner.fireEvent('initialize', this);
21002         this.pushValue();
21003     },
21004
21005     // private
21006     onDestroy : function(){
21007         
21008         
21009         
21010         if(this.rendered){
21011             
21012             //for (var i =0; i < this.toolbars.length;i++) {
21013             //    // fixme - ask toolbars for heights?
21014             //    this.toolbars[i].onDestroy();
21015            // }
21016             
21017             //this.wrap.dom.innerHTML = '';
21018             //this.wrap.remove();
21019         }
21020     },
21021
21022     // private
21023     onFirstFocus : function(){
21024         
21025         this.assignDocWin();
21026         
21027         
21028         this.activated = true;
21029          
21030     
21031         if(Roo.isGecko){ // prevent silly gecko errors
21032             this.win.focus();
21033             var s = this.win.getSelection();
21034             if(!s.focusNode || s.focusNode.nodeType != 3){
21035                 var r = s.getRangeAt(0);
21036                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21037                 r.collapse(true);
21038                 this.deferFocus();
21039             }
21040             try{
21041                 this.execCmd('useCSS', true);
21042                 this.execCmd('styleWithCSS', false);
21043             }catch(e){}
21044         }
21045         this.owner.fireEvent('activate', this);
21046     },
21047
21048     // private
21049     adjustFont: function(btn){
21050         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21051         //if(Roo.isSafari){ // safari
21052         //    adjust *= 2;
21053        // }
21054         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21055         if(Roo.isSafari){ // safari
21056             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21057             v =  (v < 10) ? 10 : v;
21058             v =  (v > 48) ? 48 : v;
21059             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21060             
21061         }
21062         
21063         
21064         v = Math.max(1, v+adjust);
21065         
21066         this.execCmd('FontSize', v  );
21067     },
21068
21069     onEditorEvent : function(e)
21070     {
21071         this.owner.fireEvent('editorevent', this, e);
21072       //  this.updateToolbar();
21073         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21074     },
21075
21076     insertTag : function(tg)
21077     {
21078         // could be a bit smarter... -> wrap the current selected tRoo..
21079         if (tg.toLowerCase() == 'span' ||
21080             tg.toLowerCase() == 'code' ||
21081             tg.toLowerCase() == 'sup' ||
21082             tg.toLowerCase() == 'sub' 
21083             ) {
21084             
21085             range = this.createRange(this.getSelection());
21086             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21087             wrappingNode.appendChild(range.extractContents());
21088             range.insertNode(wrappingNode);
21089
21090             return;
21091             
21092             
21093             
21094         }
21095         this.execCmd("formatblock",   tg);
21096         
21097     },
21098     
21099     insertText : function(txt)
21100     {
21101         
21102         
21103         var range = this.createRange();
21104         range.deleteContents();
21105                //alert(Sender.getAttribute('label'));
21106                
21107         range.insertNode(this.doc.createTextNode(txt));
21108     } ,
21109     
21110      
21111
21112     /**
21113      * Executes a Midas editor command on the editor document and performs necessary focus and
21114      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21115      * @param {String} cmd The Midas command
21116      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21117      */
21118     relayCmd : function(cmd, value){
21119         this.win.focus();
21120         this.execCmd(cmd, value);
21121         this.owner.fireEvent('editorevent', this);
21122         //this.updateToolbar();
21123         this.owner.deferFocus();
21124     },
21125
21126     /**
21127      * Executes a Midas editor command directly on the editor document.
21128      * For visual commands, you should use {@link #relayCmd} instead.
21129      * <b>This should only be called after the editor is initialized.</b>
21130      * @param {String} cmd The Midas command
21131      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21132      */
21133     execCmd : function(cmd, value){
21134         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21135         this.syncValue();
21136     },
21137  
21138  
21139    
21140     /**
21141      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21142      * to insert tRoo.
21143      * @param {String} text | dom node.. 
21144      */
21145     insertAtCursor : function(text)
21146     {
21147         
21148         if(!this.activated){
21149             return;
21150         }
21151         /*
21152         if(Roo.isIE){
21153             this.win.focus();
21154             var r = this.doc.selection.createRange();
21155             if(r){
21156                 r.collapse(true);
21157                 r.pasteHTML(text);
21158                 this.syncValue();
21159                 this.deferFocus();
21160             
21161             }
21162             return;
21163         }
21164         */
21165         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21166             this.win.focus();
21167             
21168             
21169             // from jquery ui (MIT licenced)
21170             var range, node;
21171             var win = this.win;
21172             
21173             if (win.getSelection && win.getSelection().getRangeAt) {
21174                 range = win.getSelection().getRangeAt(0);
21175                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21176                 range.insertNode(node);
21177             } else if (win.document.selection && win.document.selection.createRange) {
21178                 // no firefox support
21179                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21180                 win.document.selection.createRange().pasteHTML(txt);
21181             } else {
21182                 // no firefox support
21183                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21184                 this.execCmd('InsertHTML', txt);
21185             } 
21186             
21187             this.syncValue();
21188             
21189             this.deferFocus();
21190         }
21191     },
21192  // private
21193     mozKeyPress : function(e){
21194         if(e.ctrlKey){
21195             var c = e.getCharCode(), cmd;
21196           
21197             if(c > 0){
21198                 c = String.fromCharCode(c).toLowerCase();
21199                 switch(c){
21200                     case 'b':
21201                         cmd = 'bold';
21202                         break;
21203                     case 'i':
21204                         cmd = 'italic';
21205                         break;
21206                     
21207                     case 'u':
21208                         cmd = 'underline';
21209                         break;
21210                     
21211                     case 'v':
21212                         this.cleanUpPaste.defer(100, this);
21213                         return;
21214                         
21215                 }
21216                 if(cmd){
21217                     this.win.focus();
21218                     this.execCmd(cmd);
21219                     this.deferFocus();
21220                     e.preventDefault();
21221                 }
21222                 
21223             }
21224         }
21225     },
21226
21227     // private
21228     fixKeys : function(){ // load time branching for fastest keydown performance
21229         if(Roo.isIE){
21230             return function(e){
21231                 var k = e.getKey(), r;
21232                 if(k == e.TAB){
21233                     e.stopEvent();
21234                     r = this.doc.selection.createRange();
21235                     if(r){
21236                         r.collapse(true);
21237                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21238                         this.deferFocus();
21239                     }
21240                     return;
21241                 }
21242                 
21243                 if(k == e.ENTER){
21244                     r = this.doc.selection.createRange();
21245                     if(r){
21246                         var target = r.parentElement();
21247                         if(!target || target.tagName.toLowerCase() != 'li'){
21248                             e.stopEvent();
21249                             r.pasteHTML('<br />');
21250                             r.collapse(false);
21251                             r.select();
21252                         }
21253                     }
21254                 }
21255                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21256                     this.cleanUpPaste.defer(100, this);
21257                     return;
21258                 }
21259                 
21260                 
21261             };
21262         }else if(Roo.isOpera){
21263             return function(e){
21264                 var k = e.getKey();
21265                 if(k == e.TAB){
21266                     e.stopEvent();
21267                     this.win.focus();
21268                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21269                     this.deferFocus();
21270                 }
21271                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21272                     this.cleanUpPaste.defer(100, this);
21273                     return;
21274                 }
21275                 
21276             };
21277         }else if(Roo.isSafari){
21278             return function(e){
21279                 var k = e.getKey();
21280                 
21281                 if(k == e.TAB){
21282                     e.stopEvent();
21283                     this.execCmd('InsertText','\t');
21284                     this.deferFocus();
21285                     return;
21286                 }
21287                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21288                     this.cleanUpPaste.defer(100, this);
21289                     return;
21290                 }
21291                 
21292              };
21293         }
21294     }(),
21295     
21296     getAllAncestors: function()
21297     {
21298         var p = this.getSelectedNode();
21299         var a = [];
21300         if (!p) {
21301             a.push(p); // push blank onto stack..
21302             p = this.getParentElement();
21303         }
21304         
21305         
21306         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21307             a.push(p);
21308             p = p.parentNode;
21309         }
21310         a.push(this.doc.body);
21311         return a;
21312     },
21313     lastSel : false,
21314     lastSelNode : false,
21315     
21316     
21317     getSelection : function() 
21318     {
21319         this.assignDocWin();
21320         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21321     },
21322     
21323     getSelectedNode: function() 
21324     {
21325         // this may only work on Gecko!!!
21326         
21327         // should we cache this!!!!
21328         
21329         
21330         
21331          
21332         var range = this.createRange(this.getSelection()).cloneRange();
21333         
21334         if (Roo.isIE) {
21335             var parent = range.parentElement();
21336             while (true) {
21337                 var testRange = range.duplicate();
21338                 testRange.moveToElementText(parent);
21339                 if (testRange.inRange(range)) {
21340                     break;
21341                 }
21342                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21343                     break;
21344                 }
21345                 parent = parent.parentElement;
21346             }
21347             return parent;
21348         }
21349         
21350         // is ancestor a text element.
21351         var ac =  range.commonAncestorContainer;
21352         if (ac.nodeType == 3) {
21353             ac = ac.parentNode;
21354         }
21355         
21356         var ar = ac.childNodes;
21357          
21358         var nodes = [];
21359         var other_nodes = [];
21360         var has_other_nodes = false;
21361         for (var i=0;i<ar.length;i++) {
21362             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21363                 continue;
21364             }
21365             // fullly contained node.
21366             
21367             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21368                 nodes.push(ar[i]);
21369                 continue;
21370             }
21371             
21372             // probably selected..
21373             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21374                 other_nodes.push(ar[i]);
21375                 continue;
21376             }
21377             // outer..
21378             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21379                 continue;
21380             }
21381             
21382             
21383             has_other_nodes = true;
21384         }
21385         if (!nodes.length && other_nodes.length) {
21386             nodes= other_nodes;
21387         }
21388         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21389             return false;
21390         }
21391         
21392         return nodes[0];
21393     },
21394     createRange: function(sel)
21395     {
21396         // this has strange effects when using with 
21397         // top toolbar - not sure if it's a great idea.
21398         //this.editor.contentWindow.focus();
21399         if (typeof sel != "undefined") {
21400             try {
21401                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21402             } catch(e) {
21403                 return this.doc.createRange();
21404             }
21405         } else {
21406             return this.doc.createRange();
21407         }
21408     },
21409     getParentElement: function()
21410     {
21411         
21412         this.assignDocWin();
21413         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21414         
21415         var range = this.createRange(sel);
21416          
21417         try {
21418             var p = range.commonAncestorContainer;
21419             while (p.nodeType == 3) { // text node
21420                 p = p.parentNode;
21421             }
21422             return p;
21423         } catch (e) {
21424             return null;
21425         }
21426     
21427     },
21428     /***
21429      *
21430      * Range intersection.. the hard stuff...
21431      *  '-1' = before
21432      *  '0' = hits..
21433      *  '1' = after.
21434      *         [ -- selected range --- ]
21435      *   [fail]                        [fail]
21436      *
21437      *    basically..
21438      *      if end is before start or  hits it. fail.
21439      *      if start is after end or hits it fail.
21440      *
21441      *   if either hits (but other is outside. - then it's not 
21442      *   
21443      *    
21444      **/
21445     
21446     
21447     // @see http://www.thismuchiknow.co.uk/?p=64.
21448     rangeIntersectsNode : function(range, node)
21449     {
21450         var nodeRange = node.ownerDocument.createRange();
21451         try {
21452             nodeRange.selectNode(node);
21453         } catch (e) {
21454             nodeRange.selectNodeContents(node);
21455         }
21456     
21457         var rangeStartRange = range.cloneRange();
21458         rangeStartRange.collapse(true);
21459     
21460         var rangeEndRange = range.cloneRange();
21461         rangeEndRange.collapse(false);
21462     
21463         var nodeStartRange = nodeRange.cloneRange();
21464         nodeStartRange.collapse(true);
21465     
21466         var nodeEndRange = nodeRange.cloneRange();
21467         nodeEndRange.collapse(false);
21468     
21469         return rangeStartRange.compareBoundaryPoints(
21470                  Range.START_TO_START, nodeEndRange) == -1 &&
21471                rangeEndRange.compareBoundaryPoints(
21472                  Range.START_TO_START, nodeStartRange) == 1;
21473         
21474          
21475     },
21476     rangeCompareNode : function(range, node)
21477     {
21478         var nodeRange = node.ownerDocument.createRange();
21479         try {
21480             nodeRange.selectNode(node);
21481         } catch (e) {
21482             nodeRange.selectNodeContents(node);
21483         }
21484         
21485         
21486         range.collapse(true);
21487     
21488         nodeRange.collapse(true);
21489      
21490         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21491         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21492          
21493         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21494         
21495         var nodeIsBefore   =  ss == 1;
21496         var nodeIsAfter    = ee == -1;
21497         
21498         if (nodeIsBefore && nodeIsAfter) {
21499             return 0; // outer
21500         }
21501         if (!nodeIsBefore && nodeIsAfter) {
21502             return 1; //right trailed.
21503         }
21504         
21505         if (nodeIsBefore && !nodeIsAfter) {
21506             return 2;  // left trailed.
21507         }
21508         // fully contined.
21509         return 3;
21510     },
21511
21512     // private? - in a new class?
21513     cleanUpPaste :  function()
21514     {
21515         // cleans up the whole document..
21516         Roo.log('cleanuppaste');
21517         
21518         this.cleanUpChildren(this.doc.body);
21519         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21520         if (clean != this.doc.body.innerHTML) {
21521             this.doc.body.innerHTML = clean;
21522         }
21523         
21524     },
21525     
21526     cleanWordChars : function(input) {// change the chars to hex code
21527         var he = Roo.HtmlEditorCore;
21528         
21529         var output = input;
21530         Roo.each(he.swapCodes, function(sw) { 
21531             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21532             
21533             output = output.replace(swapper, sw[1]);
21534         });
21535         
21536         return output;
21537     },
21538     
21539     
21540     cleanUpChildren : function (n)
21541     {
21542         if (!n.childNodes.length) {
21543             return;
21544         }
21545         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21546            this.cleanUpChild(n.childNodes[i]);
21547         }
21548     },
21549     
21550     
21551         
21552     
21553     cleanUpChild : function (node)
21554     {
21555         var ed = this;
21556         //console.log(node);
21557         if (node.nodeName == "#text") {
21558             // clean up silly Windows -- stuff?
21559             return; 
21560         }
21561         if (node.nodeName == "#comment") {
21562             if (!this.allowComments) {
21563                 node.parentNode.removeChild(node);
21564             }
21565             // clean up silly Windows -- stuff?
21566             return; 
21567         }
21568         var lcname = node.tagName.toLowerCase();
21569         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21570         // whitelist of tags..
21571         
21572         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21573             // remove node.
21574             node.parentNode.removeChild(node);
21575             return;
21576             
21577         }
21578         
21579         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21580         
21581         // spans with no attributes - just remove them..
21582         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21583             remove_keep_children = true;
21584         }
21585         
21586         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21587         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21588         
21589         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21590         //    remove_keep_children = true;
21591         //}
21592         
21593         if (remove_keep_children) {
21594             this.cleanUpChildren(node);
21595             // inserts everything just before this node...
21596             while (node.childNodes.length) {
21597                 var cn = node.childNodes[0];
21598                 node.removeChild(cn);
21599                 node.parentNode.insertBefore(cn, node);
21600             }
21601             node.parentNode.removeChild(node);
21602             return;
21603         }
21604         
21605         if (!node.attributes || !node.attributes.length) {
21606             
21607           
21608             
21609             
21610             this.cleanUpChildren(node);
21611             return;
21612         }
21613         
21614         function cleanAttr(n,v)
21615         {
21616             
21617             if (v.match(/^\./) || v.match(/^\//)) {
21618                 return;
21619             }
21620             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21621                 return;
21622             }
21623             if (v.match(/^#/)) {
21624                 return;
21625             }
21626             if (v.match(/^\{/)) { // allow template editing.
21627                 return;
21628             }
21629 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21630             node.removeAttribute(n);
21631             
21632         }
21633         
21634         var cwhite = this.cwhite;
21635         var cblack = this.cblack;
21636             
21637         function cleanStyle(n,v)
21638         {
21639             if (v.match(/expression/)) { //XSS?? should we even bother..
21640                 node.removeAttribute(n);
21641                 return;
21642             }
21643             
21644             var parts = v.split(/;/);
21645             var clean = [];
21646             
21647             Roo.each(parts, function(p) {
21648                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21649                 if (!p.length) {
21650                     return true;
21651                 }
21652                 var l = p.split(':').shift().replace(/\s+/g,'');
21653                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21654                 
21655                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21656 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21657                     //node.removeAttribute(n);
21658                     return true;
21659                 }
21660                 //Roo.log()
21661                 // only allow 'c whitelisted system attributes'
21662                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21663 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21664                     //node.removeAttribute(n);
21665                     return true;
21666                 }
21667                 
21668                 
21669                  
21670                 
21671                 clean.push(p);
21672                 return true;
21673             });
21674             if (clean.length) { 
21675                 node.setAttribute(n, clean.join(';'));
21676             } else {
21677                 node.removeAttribute(n);
21678             }
21679             
21680         }
21681         
21682         
21683         for (var i = node.attributes.length-1; i > -1 ; i--) {
21684             var a = node.attributes[i];
21685             //console.log(a);
21686             
21687             if (a.name.toLowerCase().substr(0,2)=='on')  {
21688                 node.removeAttribute(a.name);
21689                 continue;
21690             }
21691             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21692                 node.removeAttribute(a.name);
21693                 continue;
21694             }
21695             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21696                 cleanAttr(a.name,a.value); // fixme..
21697                 continue;
21698             }
21699             if (a.name == 'style') {
21700                 cleanStyle(a.name,a.value);
21701                 continue;
21702             }
21703             /// clean up MS crap..
21704             // tecnically this should be a list of valid class'es..
21705             
21706             
21707             if (a.name == 'class') {
21708                 if (a.value.match(/^Mso/)) {
21709                     node.removeAttribute('class');
21710                 }
21711                 
21712                 if (a.value.match(/^body$/)) {
21713                     node.removeAttribute('class');
21714                 }
21715                 continue;
21716             }
21717             
21718             // style cleanup!?
21719             // class cleanup?
21720             
21721         }
21722         
21723         
21724         this.cleanUpChildren(node);
21725         
21726         
21727     },
21728     
21729     /**
21730      * Clean up MS wordisms...
21731      */
21732     cleanWord : function(node)
21733     {
21734         if (!node) {
21735             this.cleanWord(this.doc.body);
21736             return;
21737         }
21738         
21739         if(
21740                 node.nodeName == 'SPAN' &&
21741                 !node.hasAttributes() &&
21742                 node.childNodes.length == 1 &&
21743                 node.firstChild.nodeName == "#text"  
21744         ) {
21745             var textNode = node.firstChild;
21746             node.removeChild(textNode);
21747             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21748                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21749             }
21750             node.parentNode.insertBefore(textNode, node);
21751             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21752                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21753             }
21754             node.parentNode.removeChild(node);
21755         }
21756         
21757         if (node.nodeName == "#text") {
21758             // clean up silly Windows -- stuff?
21759             return; 
21760         }
21761         if (node.nodeName == "#comment") {
21762             node.parentNode.removeChild(node);
21763             // clean up silly Windows -- stuff?
21764             return; 
21765         }
21766         
21767         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21768             node.parentNode.removeChild(node);
21769             return;
21770         }
21771         //Roo.log(node.tagName);
21772         // remove - but keep children..
21773         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21774             //Roo.log('-- removed');
21775             while (node.childNodes.length) {
21776                 var cn = node.childNodes[0];
21777                 node.removeChild(cn);
21778                 node.parentNode.insertBefore(cn, node);
21779                 // move node to parent - and clean it..
21780                 this.cleanWord(cn);
21781             }
21782             node.parentNode.removeChild(node);
21783             /// no need to iterate chidlren = it's got none..
21784             //this.iterateChildren(node, this.cleanWord);
21785             return;
21786         }
21787         // clean styles
21788         if (node.className.length) {
21789             
21790             var cn = node.className.split(/\W+/);
21791             var cna = [];
21792             Roo.each(cn, function(cls) {
21793                 if (cls.match(/Mso[a-zA-Z]+/)) {
21794                     return;
21795                 }
21796                 cna.push(cls);
21797             });
21798             node.className = cna.length ? cna.join(' ') : '';
21799             if (!cna.length) {
21800                 node.removeAttribute("class");
21801             }
21802         }
21803         
21804         if (node.hasAttribute("lang")) {
21805             node.removeAttribute("lang");
21806         }
21807         
21808         if (node.hasAttribute("style")) {
21809             
21810             var styles = node.getAttribute("style").split(";");
21811             var nstyle = [];
21812             Roo.each(styles, function(s) {
21813                 if (!s.match(/:/)) {
21814                     return;
21815                 }
21816                 var kv = s.split(":");
21817                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21818                     return;
21819                 }
21820                 // what ever is left... we allow.
21821                 nstyle.push(s);
21822             });
21823             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21824             if (!nstyle.length) {
21825                 node.removeAttribute('style');
21826             }
21827         }
21828         this.iterateChildren(node, this.cleanWord);
21829         
21830         
21831         
21832     },
21833     /**
21834      * iterateChildren of a Node, calling fn each time, using this as the scole..
21835      * @param {DomNode} node node to iterate children of.
21836      * @param {Function} fn method of this class to call on each item.
21837      */
21838     iterateChildren : function(node, fn)
21839     {
21840         if (!node.childNodes.length) {
21841                 return;
21842         }
21843         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21844            fn.call(this, node.childNodes[i])
21845         }
21846     },
21847     
21848     
21849     /**
21850      * cleanTableWidths.
21851      *
21852      * Quite often pasting from word etc.. results in tables with column and widths.
21853      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21854      *
21855      */
21856     cleanTableWidths : function(node)
21857     {
21858          
21859          
21860         if (!node) {
21861             this.cleanTableWidths(this.doc.body);
21862             return;
21863         }
21864         
21865         // ignore list...
21866         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21867             return; 
21868         }
21869         Roo.log(node.tagName);
21870         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21871             this.iterateChildren(node, this.cleanTableWidths);
21872             return;
21873         }
21874         if (node.hasAttribute('width')) {
21875             node.removeAttribute('width');
21876         }
21877         
21878          
21879         if (node.hasAttribute("style")) {
21880             // pretty basic...
21881             
21882             var styles = node.getAttribute("style").split(";");
21883             var nstyle = [];
21884             Roo.each(styles, function(s) {
21885                 if (!s.match(/:/)) {
21886                     return;
21887                 }
21888                 var kv = s.split(":");
21889                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21890                     return;
21891                 }
21892                 // what ever is left... we allow.
21893                 nstyle.push(s);
21894             });
21895             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21896             if (!nstyle.length) {
21897                 node.removeAttribute('style');
21898             }
21899         }
21900         
21901         this.iterateChildren(node, this.cleanTableWidths);
21902         
21903         
21904     },
21905     
21906     
21907     
21908     
21909     domToHTML : function(currentElement, depth, nopadtext) {
21910         
21911         depth = depth || 0;
21912         nopadtext = nopadtext || false;
21913     
21914         if (!currentElement) {
21915             return this.domToHTML(this.doc.body);
21916         }
21917         
21918         //Roo.log(currentElement);
21919         var j;
21920         var allText = false;
21921         var nodeName = currentElement.nodeName;
21922         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21923         
21924         if  (nodeName == '#text') {
21925             
21926             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21927         }
21928         
21929         
21930         var ret = '';
21931         if (nodeName != 'BODY') {
21932              
21933             var i = 0;
21934             // Prints the node tagName, such as <A>, <IMG>, etc
21935             if (tagName) {
21936                 var attr = [];
21937                 for(i = 0; i < currentElement.attributes.length;i++) {
21938                     // quoting?
21939                     var aname = currentElement.attributes.item(i).name;
21940                     if (!currentElement.attributes.item(i).value.length) {
21941                         continue;
21942                     }
21943                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21944                 }
21945                 
21946                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21947             } 
21948             else {
21949                 
21950                 // eack
21951             }
21952         } else {
21953             tagName = false;
21954         }
21955         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21956             return ret;
21957         }
21958         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21959             nopadtext = true;
21960         }
21961         
21962         
21963         // Traverse the tree
21964         i = 0;
21965         var currentElementChild = currentElement.childNodes.item(i);
21966         var allText = true;
21967         var innerHTML  = '';
21968         lastnode = '';
21969         while (currentElementChild) {
21970             // Formatting code (indent the tree so it looks nice on the screen)
21971             var nopad = nopadtext;
21972             if (lastnode == 'SPAN') {
21973                 nopad  = true;
21974             }
21975             // text
21976             if  (currentElementChild.nodeName == '#text') {
21977                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21978                 toadd = nopadtext ? toadd : toadd.trim();
21979                 if (!nopad && toadd.length > 80) {
21980                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21981                 }
21982                 innerHTML  += toadd;
21983                 
21984                 i++;
21985                 currentElementChild = currentElement.childNodes.item(i);
21986                 lastNode = '';
21987                 continue;
21988             }
21989             allText = false;
21990             
21991             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21992                 
21993             // Recursively traverse the tree structure of the child node
21994             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21995             lastnode = currentElementChild.nodeName;
21996             i++;
21997             currentElementChild=currentElement.childNodes.item(i);
21998         }
21999         
22000         ret += innerHTML;
22001         
22002         if (!allText) {
22003                 // The remaining code is mostly for formatting the tree
22004             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22005         }
22006         
22007         
22008         if (tagName) {
22009             ret+= "</"+tagName+">";
22010         }
22011         return ret;
22012         
22013     },
22014         
22015     applyBlacklists : function()
22016     {
22017         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22018         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22019         
22020         this.white = [];
22021         this.black = [];
22022         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22023             if (b.indexOf(tag) > -1) {
22024                 return;
22025             }
22026             this.white.push(tag);
22027             
22028         }, this);
22029         
22030         Roo.each(w, function(tag) {
22031             if (b.indexOf(tag) > -1) {
22032                 return;
22033             }
22034             if (this.white.indexOf(tag) > -1) {
22035                 return;
22036             }
22037             this.white.push(tag);
22038             
22039         }, this);
22040         
22041         
22042         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22043             if (w.indexOf(tag) > -1) {
22044                 return;
22045             }
22046             this.black.push(tag);
22047             
22048         }, this);
22049         
22050         Roo.each(b, function(tag) {
22051             if (w.indexOf(tag) > -1) {
22052                 return;
22053             }
22054             if (this.black.indexOf(tag) > -1) {
22055                 return;
22056             }
22057             this.black.push(tag);
22058             
22059         }, this);
22060         
22061         
22062         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22063         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22064         
22065         this.cwhite = [];
22066         this.cblack = [];
22067         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22068             if (b.indexOf(tag) > -1) {
22069                 return;
22070             }
22071             this.cwhite.push(tag);
22072             
22073         }, this);
22074         
22075         Roo.each(w, function(tag) {
22076             if (b.indexOf(tag) > -1) {
22077                 return;
22078             }
22079             if (this.cwhite.indexOf(tag) > -1) {
22080                 return;
22081             }
22082             this.cwhite.push(tag);
22083             
22084         }, this);
22085         
22086         
22087         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22088             if (w.indexOf(tag) > -1) {
22089                 return;
22090             }
22091             this.cblack.push(tag);
22092             
22093         }, this);
22094         
22095         Roo.each(b, function(tag) {
22096             if (w.indexOf(tag) > -1) {
22097                 return;
22098             }
22099             if (this.cblack.indexOf(tag) > -1) {
22100                 return;
22101             }
22102             this.cblack.push(tag);
22103             
22104         }, this);
22105     },
22106     
22107     setStylesheets : function(stylesheets)
22108     {
22109         if(typeof(stylesheets) == 'string'){
22110             Roo.get(this.iframe.contentDocument.head).createChild({
22111                 tag : 'link',
22112                 rel : 'stylesheet',
22113                 type : 'text/css',
22114                 href : stylesheets
22115             });
22116             
22117             return;
22118         }
22119         var _this = this;
22120      
22121         Roo.each(stylesheets, function(s) {
22122             if(!s.length){
22123                 return;
22124             }
22125             
22126             Roo.get(_this.iframe.contentDocument.head).createChild({
22127                 tag : 'link',
22128                 rel : 'stylesheet',
22129                 type : 'text/css',
22130                 href : s
22131             });
22132         });
22133
22134         
22135     },
22136     
22137     removeStylesheets : function()
22138     {
22139         var _this = this;
22140         
22141         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22142             s.remove();
22143         });
22144     },
22145     
22146     setStyle : function(style)
22147     {
22148         Roo.get(this.iframe.contentDocument.head).createChild({
22149             tag : 'style',
22150             type : 'text/css',
22151             html : style
22152         });
22153
22154         return;
22155     }
22156     
22157     // hide stuff that is not compatible
22158     /**
22159      * @event blur
22160      * @hide
22161      */
22162     /**
22163      * @event change
22164      * @hide
22165      */
22166     /**
22167      * @event focus
22168      * @hide
22169      */
22170     /**
22171      * @event specialkey
22172      * @hide
22173      */
22174     /**
22175      * @cfg {String} fieldClass @hide
22176      */
22177     /**
22178      * @cfg {String} focusClass @hide
22179      */
22180     /**
22181      * @cfg {String} autoCreate @hide
22182      */
22183     /**
22184      * @cfg {String} inputType @hide
22185      */
22186     /**
22187      * @cfg {String} invalidClass @hide
22188      */
22189     /**
22190      * @cfg {String} invalidText @hide
22191      */
22192     /**
22193      * @cfg {String} msgFx @hide
22194      */
22195     /**
22196      * @cfg {String} validateOnBlur @hide
22197      */
22198 });
22199
22200 Roo.HtmlEditorCore.white = [
22201         'area', 'br', 'img', 'input', 'hr', 'wbr',
22202         
22203        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22204        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22205        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22206        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22207        'table',   'ul',         'xmp', 
22208        
22209        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22210       'thead',   'tr', 
22211      
22212       'dir', 'menu', 'ol', 'ul', 'dl',
22213        
22214       'embed',  'object'
22215 ];
22216
22217
22218 Roo.HtmlEditorCore.black = [
22219     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22220         'applet', // 
22221         'base',   'basefont', 'bgsound', 'blink',  'body', 
22222         'frame',  'frameset', 'head',    'html',   'ilayer', 
22223         'iframe', 'layer',  'link',     'meta',    'object',   
22224         'script', 'style' ,'title',  'xml' // clean later..
22225 ];
22226 Roo.HtmlEditorCore.clean = [
22227     'script', 'style', 'title', 'xml'
22228 ];
22229 Roo.HtmlEditorCore.remove = [
22230     'font'
22231 ];
22232 // attributes..
22233
22234 Roo.HtmlEditorCore.ablack = [
22235     'on'
22236 ];
22237     
22238 Roo.HtmlEditorCore.aclean = [ 
22239     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22240 ];
22241
22242 // protocols..
22243 Roo.HtmlEditorCore.pwhite= [
22244         'http',  'https',  'mailto'
22245 ];
22246
22247 // white listed style attributes.
22248 Roo.HtmlEditorCore.cwhite= [
22249       //  'text-align', /// default is to allow most things..
22250       
22251          
22252 //        'font-size'//??
22253 ];
22254
22255 // black listed style attributes.
22256 Roo.HtmlEditorCore.cblack= [
22257       //  'font-size' -- this can be set by the project 
22258 ];
22259
22260
22261 Roo.HtmlEditorCore.swapCodes   =[ 
22262     [    8211, "&#8211;" ], 
22263     [    8212, "&#8212;" ], 
22264     [    8216,  "'" ],  
22265     [    8217, "'" ],  
22266     [    8220, '"' ],  
22267     [    8221, '"' ],  
22268     [    8226, "*" ],  
22269     [    8230, "..." ]
22270 ]; 
22271
22272     //<script type="text/javascript">
22273
22274 /*
22275  * Ext JS Library 1.1.1
22276  * Copyright(c) 2006-2007, Ext JS, LLC.
22277  * Licence LGPL
22278  * 
22279  */
22280  
22281  
22282 Roo.form.HtmlEditor = function(config){
22283     
22284     
22285     
22286     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22287     
22288     if (!this.toolbars) {
22289         this.toolbars = [];
22290     }
22291     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22292     
22293     
22294 };
22295
22296 /**
22297  * @class Roo.form.HtmlEditor
22298  * @extends Roo.form.Field
22299  * Provides a lightweight HTML Editor component.
22300  *
22301  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22302  * 
22303  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22304  * supported by this editor.</b><br/><br/>
22305  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22306  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22307  */
22308 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22309     /**
22310      * @cfg {Boolean} clearUp
22311      */
22312     clearUp : true,
22313       /**
22314      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22315      */
22316     toolbars : false,
22317    
22318      /**
22319      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22320      *                        Roo.resizable.
22321      */
22322     resizable : false,
22323      /**
22324      * @cfg {Number} height (in pixels)
22325      */   
22326     height: 300,
22327    /**
22328      * @cfg {Number} width (in pixels)
22329      */   
22330     width: 500,
22331     
22332     /**
22333      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22334      * 
22335      */
22336     stylesheets: false,
22337     
22338     
22339      /**
22340      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22341      * 
22342      */
22343     cblack: false,
22344     /**
22345      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22346      * 
22347      */
22348     cwhite: false,
22349     
22350      /**
22351      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22352      * 
22353      */
22354     black: false,
22355     /**
22356      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22357      * 
22358      */
22359     white: false,
22360     /**
22361      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22362      */
22363     allowComments: false,
22364     
22365     // id of frame..
22366     frameId: false,
22367     
22368     // private properties
22369     validationEvent : false,
22370     deferHeight: true,
22371     initialized : false,
22372     activated : false,
22373     
22374     onFocus : Roo.emptyFn,
22375     iframePad:3,
22376     hideMode:'offsets',
22377     
22378     actionMode : 'container', // defaults to hiding it...
22379     
22380     defaultAutoCreate : { // modified by initCompnoent..
22381         tag: "textarea",
22382         style:"width:500px;height:300px;",
22383         autocomplete: "new-password"
22384     },
22385
22386     // private
22387     initComponent : function(){
22388         this.addEvents({
22389             /**
22390              * @event initialize
22391              * Fires when the editor is fully initialized (including the iframe)
22392              * @param {HtmlEditor} this
22393              */
22394             initialize: true,
22395             /**
22396              * @event activate
22397              * Fires when the editor is first receives the focus. Any insertion must wait
22398              * until after this event.
22399              * @param {HtmlEditor} this
22400              */
22401             activate: true,
22402              /**
22403              * @event beforesync
22404              * Fires before the textarea is updated with content from the editor iframe. Return false
22405              * to cancel the sync.
22406              * @param {HtmlEditor} this
22407              * @param {String} html
22408              */
22409             beforesync: true,
22410              /**
22411              * @event beforepush
22412              * Fires before the iframe editor is updated with content from the textarea. Return false
22413              * to cancel the push.
22414              * @param {HtmlEditor} this
22415              * @param {String} html
22416              */
22417             beforepush: true,
22418              /**
22419              * @event sync
22420              * Fires when the textarea is updated with content from the editor iframe.
22421              * @param {HtmlEditor} this
22422              * @param {String} html
22423              */
22424             sync: true,
22425              /**
22426              * @event push
22427              * Fires when the iframe editor is updated with content from the textarea.
22428              * @param {HtmlEditor} this
22429              * @param {String} html
22430              */
22431             push: true,
22432              /**
22433              * @event editmodechange
22434              * Fires when the editor switches edit modes
22435              * @param {HtmlEditor} this
22436              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22437              */
22438             editmodechange: true,
22439             /**
22440              * @event editorevent
22441              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22442              * @param {HtmlEditor} this
22443              */
22444             editorevent: true,
22445             /**
22446              * @event firstfocus
22447              * Fires when on first focus - needed by toolbars..
22448              * @param {HtmlEditor} this
22449              */
22450             firstfocus: true,
22451             /**
22452              * @event autosave
22453              * Auto save the htmlEditor value as a file into Events
22454              * @param {HtmlEditor} this
22455              */
22456             autosave: true,
22457             /**
22458              * @event savedpreview
22459              * preview the saved version of htmlEditor
22460              * @param {HtmlEditor} this
22461              */
22462             savedpreview: true,
22463             
22464             /**
22465             * @event stylesheetsclick
22466             * Fires when press the Sytlesheets button
22467             * @param {Roo.HtmlEditorCore} this
22468             */
22469             stylesheetsclick: true
22470         });
22471         this.defaultAutoCreate =  {
22472             tag: "textarea",
22473             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22474             autocomplete: "new-password"
22475         };
22476     },
22477
22478     /**
22479      * Protected method that will not generally be called directly. It
22480      * is called when the editor creates its toolbar. Override this method if you need to
22481      * add custom toolbar buttons.
22482      * @param {HtmlEditor} editor
22483      */
22484     createToolbar : function(editor){
22485         Roo.log("create toolbars");
22486         if (!editor.toolbars || !editor.toolbars.length) {
22487             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22488         }
22489         
22490         for (var i =0 ; i < editor.toolbars.length;i++) {
22491             editor.toolbars[i] = Roo.factory(
22492                     typeof(editor.toolbars[i]) == 'string' ?
22493                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22494                 Roo.form.HtmlEditor);
22495             editor.toolbars[i].init(editor);
22496         }
22497          
22498         
22499     },
22500
22501      
22502     // private
22503     onRender : function(ct, position)
22504     {
22505         var _t = this;
22506         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22507         
22508         this.wrap = this.el.wrap({
22509             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22510         });
22511         
22512         this.editorcore.onRender(ct, position);
22513          
22514         if (this.resizable) {
22515             this.resizeEl = new Roo.Resizable(this.wrap, {
22516                 pinned : true,
22517                 wrap: true,
22518                 dynamic : true,
22519                 minHeight : this.height,
22520                 height: this.height,
22521                 handles : this.resizable,
22522                 width: this.width,
22523                 listeners : {
22524                     resize : function(r, w, h) {
22525                         _t.onResize(w,h); // -something
22526                     }
22527                 }
22528             });
22529             
22530         }
22531         this.createToolbar(this);
22532        
22533         
22534         if(!this.width){
22535             this.setSize(this.wrap.getSize());
22536         }
22537         if (this.resizeEl) {
22538             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22539             // should trigger onReize..
22540         }
22541         
22542         this.keyNav = new Roo.KeyNav(this.el, {
22543             
22544             "tab" : function(e){
22545                 e.preventDefault();
22546                 
22547                 var value = this.getValue();
22548                 
22549                 var start = this.el.dom.selectionStart;
22550                 var end = this.el.dom.selectionEnd;
22551                 
22552                 if(!e.shiftKey){
22553                     
22554                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22555                     this.el.dom.setSelectionRange(end + 1, end + 1);
22556                     return;
22557                 }
22558                 
22559                 var f = value.substring(0, start).split("\t");
22560                 
22561                 if(f.pop().length != 0){
22562                     return;
22563                 }
22564                 
22565                 this.setValue(f.join("\t") + value.substring(end));
22566                 this.el.dom.setSelectionRange(start - 1, start - 1);
22567                 
22568             },
22569             
22570             "home" : function(e){
22571                 e.preventDefault();
22572                 
22573                 var curr = this.el.dom.selectionStart;
22574                 var lines = this.getValue().split("\n");
22575                 
22576                 if(!lines.length){
22577                     return;
22578                 }
22579                 
22580                 if(e.ctrlKey){
22581                     this.el.dom.setSelectionRange(0, 0);
22582                     return;
22583                 }
22584                 
22585                 var pos = 0;
22586                 
22587                 for (var i = 0; i < lines.length;i++) {
22588                     pos += lines[i].length;
22589                     
22590                     if(i != 0){
22591                         pos += 1;
22592                     }
22593                     
22594                     if(pos < curr){
22595                         continue;
22596                     }
22597                     
22598                     pos -= lines[i].length;
22599                     
22600                     break;
22601                 }
22602                 
22603                 if(!e.shiftKey){
22604                     this.el.dom.setSelectionRange(pos, pos);
22605                     return;
22606                 }
22607                 
22608                 this.el.dom.selectionStart = pos;
22609                 this.el.dom.selectionEnd = curr;
22610             },
22611             
22612             "end" : function(e){
22613                 e.preventDefault();
22614                 
22615                 var curr = this.el.dom.selectionStart;
22616                 var lines = this.getValue().split("\n");
22617                 
22618                 if(!lines.length){
22619                     return;
22620                 }
22621                 
22622                 if(e.ctrlKey){
22623                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22624                     return;
22625                 }
22626                 
22627                 var pos = 0;
22628                 
22629                 for (var i = 0; i < lines.length;i++) {
22630                     
22631                     pos += lines[i].length;
22632                     
22633                     if(i != 0){
22634                         pos += 1;
22635                     }
22636                     
22637                     if(pos < curr){
22638                         continue;
22639                     }
22640                     
22641                     break;
22642                 }
22643                 
22644                 if(!e.shiftKey){
22645                     this.el.dom.setSelectionRange(pos, pos);
22646                     return;
22647                 }
22648                 
22649                 this.el.dom.selectionStart = curr;
22650                 this.el.dom.selectionEnd = pos;
22651             },
22652
22653             scope : this,
22654
22655             doRelay : function(foo, bar, hname){
22656                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22657             },
22658
22659             forceKeyDown: true
22660         });
22661         
22662 //        if(this.autosave && this.w){
22663 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22664 //        }
22665     },
22666
22667     // private
22668     onResize : function(w, h)
22669     {
22670         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22671         var ew = false;
22672         var eh = false;
22673         
22674         if(this.el ){
22675             if(typeof w == 'number'){
22676                 var aw = w - this.wrap.getFrameWidth('lr');
22677                 this.el.setWidth(this.adjustWidth('textarea', aw));
22678                 ew = aw;
22679             }
22680             if(typeof h == 'number'){
22681                 var tbh = 0;
22682                 for (var i =0; i < this.toolbars.length;i++) {
22683                     // fixme - ask toolbars for heights?
22684                     tbh += this.toolbars[i].tb.el.getHeight();
22685                     if (this.toolbars[i].footer) {
22686                         tbh += this.toolbars[i].footer.el.getHeight();
22687                     }
22688                 }
22689                 
22690                 
22691                 
22692                 
22693                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22694                 ah -= 5; // knock a few pixes off for look..
22695 //                Roo.log(ah);
22696                 this.el.setHeight(this.adjustWidth('textarea', ah));
22697                 var eh = ah;
22698             }
22699         }
22700         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22701         this.editorcore.onResize(ew,eh);
22702         
22703     },
22704
22705     /**
22706      * Toggles the editor between standard and source edit mode.
22707      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22708      */
22709     toggleSourceEdit : function(sourceEditMode)
22710     {
22711         this.editorcore.toggleSourceEdit(sourceEditMode);
22712         
22713         if(this.editorcore.sourceEditMode){
22714             Roo.log('editor - showing textarea');
22715             
22716 //            Roo.log('in');
22717 //            Roo.log(this.syncValue());
22718             this.editorcore.syncValue();
22719             this.el.removeClass('x-hidden');
22720             this.el.dom.removeAttribute('tabIndex');
22721             this.el.focus();
22722             
22723             for (var i = 0; i < this.toolbars.length; i++) {
22724                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22725                     this.toolbars[i].tb.hide();
22726                     this.toolbars[i].footer.hide();
22727                 }
22728             }
22729             
22730         }else{
22731             Roo.log('editor - hiding textarea');
22732 //            Roo.log('out')
22733 //            Roo.log(this.pushValue()); 
22734             this.editorcore.pushValue();
22735             
22736             this.el.addClass('x-hidden');
22737             this.el.dom.setAttribute('tabIndex', -1);
22738             
22739             for (var i = 0; i < this.toolbars.length; i++) {
22740                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22741                     this.toolbars[i].tb.show();
22742                     this.toolbars[i].footer.show();
22743                 }
22744             }
22745             
22746             //this.deferFocus();
22747         }
22748         
22749         this.setSize(this.wrap.getSize());
22750         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22751         
22752         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22753     },
22754  
22755     // private (for BoxComponent)
22756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22757
22758     // private (for BoxComponent)
22759     getResizeEl : function(){
22760         return this.wrap;
22761     },
22762
22763     // private (for BoxComponent)
22764     getPositionEl : function(){
22765         return this.wrap;
22766     },
22767
22768     // private
22769     initEvents : function(){
22770         this.originalValue = this.getValue();
22771     },
22772
22773     /**
22774      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22775      * @method
22776      */
22777     markInvalid : Roo.emptyFn,
22778     /**
22779      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22780      * @method
22781      */
22782     clearInvalid : Roo.emptyFn,
22783
22784     setValue : function(v){
22785         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22786         this.editorcore.pushValue();
22787     },
22788
22789      
22790     // private
22791     deferFocus : function(){
22792         this.focus.defer(10, this);
22793     },
22794
22795     // doc'ed in Field
22796     focus : function(){
22797         this.editorcore.focus();
22798         
22799     },
22800       
22801
22802     // private
22803     onDestroy : function(){
22804         
22805         
22806         
22807         if(this.rendered){
22808             
22809             for (var i =0; i < this.toolbars.length;i++) {
22810                 // fixme - ask toolbars for heights?
22811                 this.toolbars[i].onDestroy();
22812             }
22813             
22814             this.wrap.dom.innerHTML = '';
22815             this.wrap.remove();
22816         }
22817     },
22818
22819     // private
22820     onFirstFocus : function(){
22821         //Roo.log("onFirstFocus");
22822         this.editorcore.onFirstFocus();
22823          for (var i =0; i < this.toolbars.length;i++) {
22824             this.toolbars[i].onFirstFocus();
22825         }
22826         
22827     },
22828     
22829     // private
22830     syncValue : function()
22831     {
22832         this.editorcore.syncValue();
22833     },
22834     
22835     pushValue : function()
22836     {
22837         this.editorcore.pushValue();
22838     },
22839     
22840     setStylesheets : function(stylesheets)
22841     {
22842         this.editorcore.setStylesheets(stylesheets);
22843     },
22844     
22845     removeStylesheets : function()
22846     {
22847         this.editorcore.removeStylesheets();
22848     }
22849      
22850     
22851     // hide stuff that is not compatible
22852     /**
22853      * @event blur
22854      * @hide
22855      */
22856     /**
22857      * @event change
22858      * @hide
22859      */
22860     /**
22861      * @event focus
22862      * @hide
22863      */
22864     /**
22865      * @event specialkey
22866      * @hide
22867      */
22868     /**
22869      * @cfg {String} fieldClass @hide
22870      */
22871     /**
22872      * @cfg {String} focusClass @hide
22873      */
22874     /**
22875      * @cfg {String} autoCreate @hide
22876      */
22877     /**
22878      * @cfg {String} inputType @hide
22879      */
22880     /**
22881      * @cfg {String} invalidClass @hide
22882      */
22883     /**
22884      * @cfg {String} invalidText @hide
22885      */
22886     /**
22887      * @cfg {String} msgFx @hide
22888      */
22889     /**
22890      * @cfg {String} validateOnBlur @hide
22891      */
22892 });
22893  
22894     // <script type="text/javascript">
22895 /*
22896  * Based on
22897  * Ext JS Library 1.1.1
22898  * Copyright(c) 2006-2007, Ext JS, LLC.
22899  *  
22900  
22901  */
22902
22903 /**
22904  * @class Roo.form.HtmlEditorToolbar1
22905  * Basic Toolbar
22906  * 
22907  * Usage:
22908  *
22909  new Roo.form.HtmlEditor({
22910     ....
22911     toolbars : [
22912         new Roo.form.HtmlEditorToolbar1({
22913             disable : { fonts: 1 , format: 1, ..., ... , ...],
22914             btns : [ .... ]
22915         })
22916     }
22917      
22918  * 
22919  * @cfg {Object} disable List of elements to disable..
22920  * @cfg {Array} btns List of additional buttons.
22921  * 
22922  * 
22923  * NEEDS Extra CSS? 
22924  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22925  */
22926  
22927 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22928 {
22929     
22930     Roo.apply(this, config);
22931     
22932     // default disabled, based on 'good practice'..
22933     this.disable = this.disable || {};
22934     Roo.applyIf(this.disable, {
22935         fontSize : true,
22936         colors : true,
22937         specialElements : true
22938     });
22939     
22940     
22941     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22942     // dont call parent... till later.
22943 }
22944
22945 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22946     
22947     tb: false,
22948     
22949     rendered: false,
22950     
22951     editor : false,
22952     editorcore : false,
22953     /**
22954      * @cfg {Object} disable  List of toolbar elements to disable
22955          
22956      */
22957     disable : false,
22958     
22959     
22960      /**
22961      * @cfg {String} createLinkText The default text for the create link prompt
22962      */
22963     createLinkText : 'Please enter the URL for the link:',
22964     /**
22965      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22966      */
22967     defaultLinkValue : 'http:/'+'/',
22968    
22969     
22970       /**
22971      * @cfg {Array} fontFamilies An array of available font families
22972      */
22973     fontFamilies : [
22974         'Arial',
22975         'Courier New',
22976         'Tahoma',
22977         'Times New Roman',
22978         'Verdana'
22979     ],
22980     
22981     specialChars : [
22982            "&#169;",
22983           "&#174;",     
22984           "&#8482;",    
22985           "&#163;" ,    
22986          // "&#8212;",    
22987           "&#8230;",    
22988           "&#247;" ,    
22989         //  "&#225;" ,     ?? a acute?
22990            "&#8364;"    , //Euro
22991        //   "&#8220;"    ,
22992         //  "&#8221;"    ,
22993         //  "&#8226;"    ,
22994           "&#176;"  //   , // degrees
22995
22996          // "&#233;"     , // e ecute
22997          // "&#250;"     , // u ecute?
22998     ],
22999     
23000     specialElements : [
23001         {
23002             text: "Insert Table",
23003             xtype: 'MenuItem',
23004             xns : Roo.Menu,
23005             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23006                 
23007         },
23008         {    
23009             text: "Insert Image",
23010             xtype: 'MenuItem',
23011             xns : Roo.Menu,
23012             ihtml : '<img src="about:blank"/>'
23013             
23014         }
23015         
23016          
23017     ],
23018     
23019     
23020     inputElements : [ 
23021             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23022             "input:submit", "input:button", "select", "textarea", "label" ],
23023     formats : [
23024         ["p"] ,  
23025         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23026         ["pre"],[ "code"], 
23027         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23028         ['div'],['span'],
23029         ['sup'],['sub']
23030     ],
23031     
23032     cleanStyles : [
23033         "font-size"
23034     ],
23035      /**
23036      * @cfg {String} defaultFont default font to use.
23037      */
23038     defaultFont: 'tahoma',
23039    
23040     fontSelect : false,
23041     
23042     
23043     formatCombo : false,
23044     
23045     init : function(editor)
23046     {
23047         this.editor = editor;
23048         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23049         var editorcore = this.editorcore;
23050         
23051         var _t = this;
23052         
23053         var fid = editorcore.frameId;
23054         var etb = this;
23055         function btn(id, toggle, handler){
23056             var xid = fid + '-'+ id ;
23057             return {
23058                 id : xid,
23059                 cmd : id,
23060                 cls : 'x-btn-icon x-edit-'+id,
23061                 enableToggle:toggle !== false,
23062                 scope: _t, // was editor...
23063                 handler:handler||_t.relayBtnCmd,
23064                 clickEvent:'mousedown',
23065                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23066                 tabIndex:-1
23067             };
23068         }
23069         
23070         
23071         
23072         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23073         this.tb = tb;
23074          // stop form submits
23075         tb.el.on('click', function(e){
23076             e.preventDefault(); // what does this do?
23077         });
23078
23079         if(!this.disable.font) { // && !Roo.isSafari){
23080             /* why no safari for fonts 
23081             editor.fontSelect = tb.el.createChild({
23082                 tag:'select',
23083                 tabIndex: -1,
23084                 cls:'x-font-select',
23085                 html: this.createFontOptions()
23086             });
23087             
23088             editor.fontSelect.on('change', function(){
23089                 var font = editor.fontSelect.dom.value;
23090                 editor.relayCmd('fontname', font);
23091                 editor.deferFocus();
23092             }, editor);
23093             
23094             tb.add(
23095                 editor.fontSelect.dom,
23096                 '-'
23097             );
23098             */
23099             
23100         };
23101         if(!this.disable.formats){
23102             this.formatCombo = new Roo.form.ComboBox({
23103                 store: new Roo.data.SimpleStore({
23104                     id : 'tag',
23105                     fields: ['tag'],
23106                     data : this.formats // from states.js
23107                 }),
23108                 blockFocus : true,
23109                 name : '',
23110                 //autoCreate : {tag: "div",  size: "20"},
23111                 displayField:'tag',
23112                 typeAhead: false,
23113                 mode: 'local',
23114                 editable : false,
23115                 triggerAction: 'all',
23116                 emptyText:'Add tag',
23117                 selectOnFocus:true,
23118                 width:135,
23119                 listeners : {
23120                     'select': function(c, r, i) {
23121                         editorcore.insertTag(r.get('tag'));
23122                         editor.focus();
23123                     }
23124                 }
23125
23126             });
23127             tb.addField(this.formatCombo);
23128             
23129         }
23130         
23131         if(!this.disable.format){
23132             tb.add(
23133                 btn('bold'),
23134                 btn('italic'),
23135                 btn('underline'),
23136                 btn('strikethrough')
23137             );
23138         };
23139         if(!this.disable.fontSize){
23140             tb.add(
23141                 '-',
23142                 
23143                 
23144                 btn('increasefontsize', false, editorcore.adjustFont),
23145                 btn('decreasefontsize', false, editorcore.adjustFont)
23146             );
23147         };
23148         
23149         
23150         if(!this.disable.colors){
23151             tb.add(
23152                 '-', {
23153                     id:editorcore.frameId +'-forecolor',
23154                     cls:'x-btn-icon x-edit-forecolor',
23155                     clickEvent:'mousedown',
23156                     tooltip: this.buttonTips['forecolor'] || undefined,
23157                     tabIndex:-1,
23158                     menu : new Roo.menu.ColorMenu({
23159                         allowReselect: true,
23160                         focus: Roo.emptyFn,
23161                         value:'000000',
23162                         plain:true,
23163                         selectHandler: function(cp, color){
23164                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23165                             editor.deferFocus();
23166                         },
23167                         scope: editorcore,
23168                         clickEvent:'mousedown'
23169                     })
23170                 }, {
23171                     id:editorcore.frameId +'backcolor',
23172                     cls:'x-btn-icon x-edit-backcolor',
23173                     clickEvent:'mousedown',
23174                     tooltip: this.buttonTips['backcolor'] || undefined,
23175                     tabIndex:-1,
23176                     menu : new Roo.menu.ColorMenu({
23177                         focus: Roo.emptyFn,
23178                         value:'FFFFFF',
23179                         plain:true,
23180                         allowReselect: true,
23181                         selectHandler: function(cp, color){
23182                             if(Roo.isGecko){
23183                                 editorcore.execCmd('useCSS', false);
23184                                 editorcore.execCmd('hilitecolor', color);
23185                                 editorcore.execCmd('useCSS', true);
23186                                 editor.deferFocus();
23187                             }else{
23188                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23189                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23190                                 editor.deferFocus();
23191                             }
23192                         },
23193                         scope:editorcore,
23194                         clickEvent:'mousedown'
23195                     })
23196                 }
23197             );
23198         };
23199         // now add all the items...
23200         
23201
23202         if(!this.disable.alignments){
23203             tb.add(
23204                 '-',
23205                 btn('justifyleft'),
23206                 btn('justifycenter'),
23207                 btn('justifyright')
23208             );
23209         };
23210
23211         //if(!Roo.isSafari){
23212             if(!this.disable.links){
23213                 tb.add(
23214                     '-',
23215                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23216                 );
23217             };
23218
23219             if(!this.disable.lists){
23220                 tb.add(
23221                     '-',
23222                     btn('insertorderedlist'),
23223                     btn('insertunorderedlist')
23224                 );
23225             }
23226             if(!this.disable.sourceEdit){
23227                 tb.add(
23228                     '-',
23229                     btn('sourceedit', true, function(btn){
23230                         this.toggleSourceEdit(btn.pressed);
23231                     })
23232                 );
23233             }
23234         //}
23235         
23236         var smenu = { };
23237         // special menu.. - needs to be tidied up..
23238         if (!this.disable.special) {
23239             smenu = {
23240                 text: "&#169;",
23241                 cls: 'x-edit-none',
23242                 
23243                 menu : {
23244                     items : []
23245                 }
23246             };
23247             for (var i =0; i < this.specialChars.length; i++) {
23248                 smenu.menu.items.push({
23249                     
23250                     html: this.specialChars[i],
23251                     handler: function(a,b) {
23252                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23253                         //editor.insertAtCursor(a.html);
23254                         
23255                     },
23256                     tabIndex:-1
23257                 });
23258             }
23259             
23260             
23261             tb.add(smenu);
23262             
23263             
23264         }
23265         
23266         var cmenu = { };
23267         if (!this.disable.cleanStyles) {
23268             cmenu = {
23269                 cls: 'x-btn-icon x-btn-clear',
23270                 
23271                 menu : {
23272                     items : []
23273                 }
23274             };
23275             for (var i =0; i < this.cleanStyles.length; i++) {
23276                 cmenu.menu.items.push({
23277                     actiontype : this.cleanStyles[i],
23278                     html: 'Remove ' + this.cleanStyles[i],
23279                     handler: function(a,b) {
23280 //                        Roo.log(a);
23281 //                        Roo.log(b);
23282                         var c = Roo.get(editorcore.doc.body);
23283                         c.select('[style]').each(function(s) {
23284                             s.dom.style.removeProperty(a.actiontype);
23285                         });
23286                         editorcore.syncValue();
23287                     },
23288                     tabIndex:-1
23289                 });
23290             }
23291              cmenu.menu.items.push({
23292                 actiontype : 'tablewidths',
23293                 html: 'Remove Table Widths',
23294                 handler: function(a,b) {
23295                     editorcore.cleanTableWidths();
23296                     editorcore.syncValue();
23297                 },
23298                 tabIndex:-1
23299             });
23300             cmenu.menu.items.push({
23301                 actiontype : 'word',
23302                 html: 'Remove MS Word Formating',
23303                 handler: function(a,b) {
23304                     editorcore.cleanWord();
23305                     editorcore.syncValue();
23306                 },
23307                 tabIndex:-1
23308             });
23309             
23310             cmenu.menu.items.push({
23311                 actiontype : 'all',
23312                 html: 'Remove All Styles',
23313                 handler: function(a,b) {
23314                     
23315                     var c = Roo.get(editorcore.doc.body);
23316                     c.select('[style]').each(function(s) {
23317                         s.dom.removeAttribute('style');
23318                     });
23319                     editorcore.syncValue();
23320                 },
23321                 tabIndex:-1
23322             });
23323             
23324             cmenu.menu.items.push({
23325                 actiontype : 'all',
23326                 html: 'Remove All CSS Classes',
23327                 handler: function(a,b) {
23328                     
23329                     var c = Roo.get(editorcore.doc.body);
23330                     c.select('[class]').each(function(s) {
23331                         s.dom.removeAttribute('class');
23332                     });
23333                     editorcore.cleanWord();
23334                     editorcore.syncValue();
23335                 },
23336                 tabIndex:-1
23337             });
23338             
23339              cmenu.menu.items.push({
23340                 actiontype : 'tidy',
23341                 html: 'Tidy HTML Source',
23342                 handler: function(a,b) {
23343                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23344                     editorcore.syncValue();
23345                 },
23346                 tabIndex:-1
23347             });
23348             
23349             
23350             tb.add(cmenu);
23351         }
23352          
23353         if (!this.disable.specialElements) {
23354             var semenu = {
23355                 text: "Other;",
23356                 cls: 'x-edit-none',
23357                 menu : {
23358                     items : []
23359                 }
23360             };
23361             for (var i =0; i < this.specialElements.length; i++) {
23362                 semenu.menu.items.push(
23363                     Roo.apply({ 
23364                         handler: function(a,b) {
23365                             editor.insertAtCursor(this.ihtml);
23366                         }
23367                     }, this.specialElements[i])
23368                 );
23369                     
23370             }
23371             
23372             tb.add(semenu);
23373             
23374             
23375         }
23376          
23377         
23378         if (this.btns) {
23379             for(var i =0; i< this.btns.length;i++) {
23380                 var b = Roo.factory(this.btns[i],Roo.form);
23381                 b.cls =  'x-edit-none';
23382                 
23383                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23384                     b.cls += ' x-init-enable';
23385                 }
23386                 
23387                 b.scope = editorcore;
23388                 tb.add(b);
23389             }
23390         
23391         }
23392         
23393         
23394         
23395         // disable everything...
23396         
23397         this.tb.items.each(function(item){
23398             
23399            if(
23400                 item.id != editorcore.frameId+ '-sourceedit' && 
23401                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23402             ){
23403                 
23404                 item.disable();
23405             }
23406         });
23407         this.rendered = true;
23408         
23409         // the all the btns;
23410         editor.on('editorevent', this.updateToolbar, this);
23411         // other toolbars need to implement this..
23412         //editor.on('editmodechange', this.updateToolbar, this);
23413     },
23414     
23415     
23416     relayBtnCmd : function(btn) {
23417         this.editorcore.relayCmd(btn.cmd);
23418     },
23419     // private used internally
23420     createLink : function(){
23421         Roo.log("create link?");
23422         var url = prompt(this.createLinkText, this.defaultLinkValue);
23423         if(url && url != 'http:/'+'/'){
23424             this.editorcore.relayCmd('createlink', url);
23425         }
23426     },
23427
23428     
23429     /**
23430      * Protected method that will not generally be called directly. It triggers
23431      * a toolbar update by reading the markup state of the current selection in the editor.
23432      */
23433     updateToolbar: function(){
23434
23435         if(!this.editorcore.activated){
23436             this.editor.onFirstFocus();
23437             return;
23438         }
23439
23440         var btns = this.tb.items.map, 
23441             doc = this.editorcore.doc,
23442             frameId = this.editorcore.frameId;
23443
23444         if(!this.disable.font && !Roo.isSafari){
23445             /*
23446             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23447             if(name != this.fontSelect.dom.value){
23448                 this.fontSelect.dom.value = name;
23449             }
23450             */
23451         }
23452         if(!this.disable.format){
23453             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23454             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23455             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23456             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23457         }
23458         if(!this.disable.alignments){
23459             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23460             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23461             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23462         }
23463         if(!Roo.isSafari && !this.disable.lists){
23464             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23465             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23466         }
23467         
23468         var ans = this.editorcore.getAllAncestors();
23469         if (this.formatCombo) {
23470             
23471             
23472             var store = this.formatCombo.store;
23473             this.formatCombo.setValue("");
23474             for (var i =0; i < ans.length;i++) {
23475                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23476                     // select it..
23477                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23478                     break;
23479                 }
23480             }
23481         }
23482         
23483         
23484         
23485         // hides menus... - so this cant be on a menu...
23486         Roo.menu.MenuMgr.hideAll();
23487
23488         //this.editorsyncValue();
23489     },
23490    
23491     
23492     createFontOptions : function(){
23493         var buf = [], fs = this.fontFamilies, ff, lc;
23494         
23495         
23496         
23497         for(var i = 0, len = fs.length; i< len; i++){
23498             ff = fs[i];
23499             lc = ff.toLowerCase();
23500             buf.push(
23501                 '<option value="',lc,'" style="font-family:',ff,';"',
23502                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23503                     ff,
23504                 '</option>'
23505             );
23506         }
23507         return buf.join('');
23508     },
23509     
23510     toggleSourceEdit : function(sourceEditMode){
23511         
23512         Roo.log("toolbar toogle");
23513         if(sourceEditMode === undefined){
23514             sourceEditMode = !this.sourceEditMode;
23515         }
23516         this.sourceEditMode = sourceEditMode === true;
23517         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23518         // just toggle the button?
23519         if(btn.pressed !== this.sourceEditMode){
23520             btn.toggle(this.sourceEditMode);
23521             return;
23522         }
23523         
23524         if(sourceEditMode){
23525             Roo.log("disabling buttons");
23526             this.tb.items.each(function(item){
23527                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23528                     item.disable();
23529                 }
23530             });
23531           
23532         }else{
23533             Roo.log("enabling buttons");
23534             if(this.editorcore.initialized){
23535                 this.tb.items.each(function(item){
23536                     item.enable();
23537                 });
23538             }
23539             
23540         }
23541         Roo.log("calling toggole on editor");
23542         // tell the editor that it's been pressed..
23543         this.editor.toggleSourceEdit(sourceEditMode);
23544        
23545     },
23546      /**
23547      * Object collection of toolbar tooltips for the buttons in the editor. The key
23548      * is the command id associated with that button and the value is a valid QuickTips object.
23549      * For example:
23550 <pre><code>
23551 {
23552     bold : {
23553         title: 'Bold (Ctrl+B)',
23554         text: 'Make the selected text bold.',
23555         cls: 'x-html-editor-tip'
23556     },
23557     italic : {
23558         title: 'Italic (Ctrl+I)',
23559         text: 'Make the selected text italic.',
23560         cls: 'x-html-editor-tip'
23561     },
23562     ...
23563 </code></pre>
23564     * @type Object
23565      */
23566     buttonTips : {
23567         bold : {
23568             title: 'Bold (Ctrl+B)',
23569             text: 'Make the selected text bold.',
23570             cls: 'x-html-editor-tip'
23571         },
23572         italic : {
23573             title: 'Italic (Ctrl+I)',
23574             text: 'Make the selected text italic.',
23575             cls: 'x-html-editor-tip'
23576         },
23577         underline : {
23578             title: 'Underline (Ctrl+U)',
23579             text: 'Underline the selected text.',
23580             cls: 'x-html-editor-tip'
23581         },
23582         strikethrough : {
23583             title: 'Strikethrough',
23584             text: 'Strikethrough the selected text.',
23585             cls: 'x-html-editor-tip'
23586         },
23587         increasefontsize : {
23588             title: 'Grow Text',
23589             text: 'Increase the font size.',
23590             cls: 'x-html-editor-tip'
23591         },
23592         decreasefontsize : {
23593             title: 'Shrink Text',
23594             text: 'Decrease the font size.',
23595             cls: 'x-html-editor-tip'
23596         },
23597         backcolor : {
23598             title: 'Text Highlight Color',
23599             text: 'Change the background color of the selected text.',
23600             cls: 'x-html-editor-tip'
23601         },
23602         forecolor : {
23603             title: 'Font Color',
23604             text: 'Change the color of the selected text.',
23605             cls: 'x-html-editor-tip'
23606         },
23607         justifyleft : {
23608             title: 'Align Text Left',
23609             text: 'Align text to the left.',
23610             cls: 'x-html-editor-tip'
23611         },
23612         justifycenter : {
23613             title: 'Center Text',
23614             text: 'Center text in the editor.',
23615             cls: 'x-html-editor-tip'
23616         },
23617         justifyright : {
23618             title: 'Align Text Right',
23619             text: 'Align text to the right.',
23620             cls: 'x-html-editor-tip'
23621         },
23622         insertunorderedlist : {
23623             title: 'Bullet List',
23624             text: 'Start a bulleted list.',
23625             cls: 'x-html-editor-tip'
23626         },
23627         insertorderedlist : {
23628             title: 'Numbered List',
23629             text: 'Start a numbered list.',
23630             cls: 'x-html-editor-tip'
23631         },
23632         createlink : {
23633             title: 'Hyperlink',
23634             text: 'Make the selected text a hyperlink.',
23635             cls: 'x-html-editor-tip'
23636         },
23637         sourceedit : {
23638             title: 'Source Edit',
23639             text: 'Switch to source editing mode.',
23640             cls: 'x-html-editor-tip'
23641         }
23642     },
23643     // private
23644     onDestroy : function(){
23645         if(this.rendered){
23646             
23647             this.tb.items.each(function(item){
23648                 if(item.menu){
23649                     item.menu.removeAll();
23650                     if(item.menu.el){
23651                         item.menu.el.destroy();
23652                     }
23653                 }
23654                 item.destroy();
23655             });
23656              
23657         }
23658     },
23659     onFirstFocus: function() {
23660         this.tb.items.each(function(item){
23661            item.enable();
23662         });
23663     }
23664 });
23665
23666
23667
23668
23669 // <script type="text/javascript">
23670 /*
23671  * Based on
23672  * Ext JS Library 1.1.1
23673  * Copyright(c) 2006-2007, Ext JS, LLC.
23674  *  
23675  
23676  */
23677
23678  
23679 /**
23680  * @class Roo.form.HtmlEditor.ToolbarContext
23681  * Context Toolbar
23682  * 
23683  * Usage:
23684  *
23685  new Roo.form.HtmlEditor({
23686     ....
23687     toolbars : [
23688         { xtype: 'ToolbarStandard', styles : {} }
23689         { xtype: 'ToolbarContext', disable : {} }
23690     ]
23691 })
23692
23693      
23694  * 
23695  * @config : {Object} disable List of elements to disable.. (not done yet.)
23696  * @config : {Object} styles  Map of styles available.
23697  * 
23698  */
23699
23700 Roo.form.HtmlEditor.ToolbarContext = function(config)
23701 {
23702     
23703     Roo.apply(this, config);
23704     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23705     // dont call parent... till later.
23706     this.styles = this.styles || {};
23707 }
23708
23709  
23710
23711 Roo.form.HtmlEditor.ToolbarContext.types = {
23712     'IMG' : {
23713         width : {
23714             title: "Width",
23715             width: 40
23716         },
23717         height:  {
23718             title: "Height",
23719             width: 40
23720         },
23721         align: {
23722             title: "Align",
23723             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23724             width : 80
23725             
23726         },
23727         border: {
23728             title: "Border",
23729             width: 40
23730         },
23731         alt: {
23732             title: "Alt",
23733             width: 120
23734         },
23735         src : {
23736             title: "Src",
23737             width: 220
23738         }
23739         
23740     },
23741     'A' : {
23742         name : {
23743             title: "Name",
23744             width: 50
23745         },
23746         target:  {
23747             title: "Target",
23748             width: 120
23749         },
23750         href:  {
23751             title: "Href",
23752             width: 220
23753         } // border?
23754         
23755     },
23756     'TABLE' : {
23757         rows : {
23758             title: "Rows",
23759             width: 20
23760         },
23761         cols : {
23762             title: "Cols",
23763             width: 20
23764         },
23765         width : {
23766             title: "Width",
23767             width: 40
23768         },
23769         height : {
23770             title: "Height",
23771             width: 40
23772         },
23773         border : {
23774             title: "Border",
23775             width: 20
23776         }
23777     },
23778     'TD' : {
23779         width : {
23780             title: "Width",
23781             width: 40
23782         },
23783         height : {
23784             title: "Height",
23785             width: 40
23786         },   
23787         align: {
23788             title: "Align",
23789             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23790             width: 80
23791         },
23792         valign: {
23793             title: "Valign",
23794             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23795             width: 80
23796         },
23797         colspan: {
23798             title: "Colspan",
23799             width: 20
23800             
23801         },
23802          'font-family'  : {
23803             title : "Font",
23804             style : 'fontFamily',
23805             displayField: 'display',
23806             optname : 'font-family',
23807             width: 140
23808         }
23809     },
23810     'INPUT' : {
23811         name : {
23812             title: "name",
23813             width: 120
23814         },
23815         value : {
23816             title: "Value",
23817             width: 120
23818         },
23819         width : {
23820             title: "Width",
23821             width: 40
23822         }
23823     },
23824     'LABEL' : {
23825         'for' : {
23826             title: "For",
23827             width: 120
23828         }
23829     },
23830     'TEXTAREA' : {
23831           name : {
23832             title: "name",
23833             width: 120
23834         },
23835         rows : {
23836             title: "Rows",
23837             width: 20
23838         },
23839         cols : {
23840             title: "Cols",
23841             width: 20
23842         }
23843     },
23844     'SELECT' : {
23845         name : {
23846             title: "name",
23847             width: 120
23848         },
23849         selectoptions : {
23850             title: "Options",
23851             width: 200
23852         }
23853     },
23854     
23855     // should we really allow this??
23856     // should this just be 
23857     'BODY' : {
23858         title : {
23859             title: "Title",
23860             width: 200,
23861             disabled : true
23862         }
23863     },
23864     'SPAN' : {
23865         'font-family'  : {
23866             title : "Font",
23867             style : 'fontFamily',
23868             displayField: 'display',
23869             optname : 'font-family',
23870             width: 140
23871         }
23872     },
23873     'DIV' : {
23874         'font-family'  : {
23875             title : "Font",
23876             style : 'fontFamily',
23877             displayField: 'display',
23878             optname : 'font-family',
23879             width: 140
23880         }
23881     },
23882      'P' : {
23883         'font-family'  : {
23884             title : "Font",
23885             style : 'fontFamily',
23886             displayField: 'display',
23887             optname : 'font-family',
23888             width: 140
23889         }
23890     },
23891     
23892     '*' : {
23893         // empty..
23894     }
23895
23896 };
23897
23898 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23899 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23900
23901 Roo.form.HtmlEditor.ToolbarContext.options = {
23902         'font-family'  : [ 
23903                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23904                 [ 'Courier New', 'Courier New'],
23905                 [ 'Tahoma', 'Tahoma'],
23906                 [ 'Times New Roman,serif', 'Times'],
23907                 [ 'Verdana','Verdana' ]
23908         ]
23909 };
23910
23911 // fixme - these need to be configurable..
23912  
23913
23914 //Roo.form.HtmlEditor.ToolbarContext.types
23915
23916
23917 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23918     
23919     tb: false,
23920     
23921     rendered: false,
23922     
23923     editor : false,
23924     editorcore : false,
23925     /**
23926      * @cfg {Object} disable  List of toolbar elements to disable
23927          
23928      */
23929     disable : false,
23930     /**
23931      * @cfg {Object} styles List of styles 
23932      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23933      *
23934      * These must be defined in the page, so they get rendered correctly..
23935      * .headline { }
23936      * TD.underline { }
23937      * 
23938      */
23939     styles : false,
23940     
23941     options: false,
23942     
23943     toolbars : false,
23944     
23945     init : function(editor)
23946     {
23947         this.editor = editor;
23948         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23949         var editorcore = this.editorcore;
23950         
23951         var fid = editorcore.frameId;
23952         var etb = this;
23953         function btn(id, toggle, handler){
23954             var xid = fid + '-'+ id ;
23955             return {
23956                 id : xid,
23957                 cmd : id,
23958                 cls : 'x-btn-icon x-edit-'+id,
23959                 enableToggle:toggle !== false,
23960                 scope: editorcore, // was editor...
23961                 handler:handler||editorcore.relayBtnCmd,
23962                 clickEvent:'mousedown',
23963                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23964                 tabIndex:-1
23965             };
23966         }
23967         // create a new element.
23968         var wdiv = editor.wrap.createChild({
23969                 tag: 'div'
23970             }, editor.wrap.dom.firstChild.nextSibling, true);
23971         
23972         // can we do this more than once??
23973         
23974          // stop form submits
23975       
23976  
23977         // disable everything...
23978         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23979         this.toolbars = {};
23980            
23981         for (var i in  ty) {
23982           
23983             this.toolbars[i] = this.buildToolbar(ty[i],i);
23984         }
23985         this.tb = this.toolbars.BODY;
23986         this.tb.el.show();
23987         this.buildFooter();
23988         this.footer.show();
23989         editor.on('hide', function( ) { this.footer.hide() }, this);
23990         editor.on('show', function( ) { this.footer.show() }, this);
23991         
23992          
23993         this.rendered = true;
23994         
23995         // the all the btns;
23996         editor.on('editorevent', this.updateToolbar, this);
23997         // other toolbars need to implement this..
23998         //editor.on('editmodechange', this.updateToolbar, this);
23999     },
24000     
24001     
24002     
24003     /**
24004      * Protected method that will not generally be called directly. It triggers
24005      * a toolbar update by reading the markup state of the current selection in the editor.
24006      *
24007      * Note you can force an update by calling on('editorevent', scope, false)
24008      */
24009     updateToolbar: function(editor,ev,sel){
24010
24011         //Roo.log(ev);
24012         // capture mouse up - this is handy for selecting images..
24013         // perhaps should go somewhere else...
24014         if(!this.editorcore.activated){
24015              this.editor.onFirstFocus();
24016             return;
24017         }
24018         
24019         
24020         
24021         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24022         // selectNode - might want to handle IE?
24023         if (ev &&
24024             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24025             ev.target && ev.target.tagName == 'IMG') {
24026             // they have click on an image...
24027             // let's see if we can change the selection...
24028             sel = ev.target;
24029          
24030               var nodeRange = sel.ownerDocument.createRange();
24031             try {
24032                 nodeRange.selectNode(sel);
24033             } catch (e) {
24034                 nodeRange.selectNodeContents(sel);
24035             }
24036             //nodeRange.collapse(true);
24037             var s = this.editorcore.win.getSelection();
24038             s.removeAllRanges();
24039             s.addRange(nodeRange);
24040         }  
24041         
24042       
24043         var updateFooter = sel ? false : true;
24044         
24045         
24046         var ans = this.editorcore.getAllAncestors();
24047         
24048         // pick
24049         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24050         
24051         if (!sel) { 
24052             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24053             sel = sel ? sel : this.editorcore.doc.body;
24054             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24055             
24056         }
24057         // pick a menu that exists..
24058         var tn = sel.tagName.toUpperCase();
24059         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24060         
24061         tn = sel.tagName.toUpperCase();
24062         
24063         var lastSel = this.tb.selectedNode;
24064         
24065         this.tb.selectedNode = sel;
24066         
24067         // if current menu does not match..
24068         
24069         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24070                 
24071             this.tb.el.hide();
24072             ///console.log("show: " + tn);
24073             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24074             this.tb.el.show();
24075             // update name
24076             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24077             
24078             
24079             // update attributes
24080             if (this.tb.fields) {
24081                 this.tb.fields.each(function(e) {
24082                     if (e.stylename) {
24083                         e.setValue(sel.style[e.stylename]);
24084                         return;
24085                     } 
24086                    e.setValue(sel.getAttribute(e.attrname));
24087                 });
24088             }
24089             
24090             var hasStyles = false;
24091             for(var i in this.styles) {
24092                 hasStyles = true;
24093                 break;
24094             }
24095             
24096             // update styles
24097             if (hasStyles) { 
24098                 var st = this.tb.fields.item(0);
24099                 
24100                 st.store.removeAll();
24101                
24102                 
24103                 var cn = sel.className.split(/\s+/);
24104                 
24105                 var avs = [];
24106                 if (this.styles['*']) {
24107                     
24108                     Roo.each(this.styles['*'], function(v) {
24109                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24110                     });
24111                 }
24112                 if (this.styles[tn]) { 
24113                     Roo.each(this.styles[tn], function(v) {
24114                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24115                     });
24116                 }
24117                 
24118                 st.store.loadData(avs);
24119                 st.collapse();
24120                 st.setValue(cn);
24121             }
24122             // flag our selected Node.
24123             this.tb.selectedNode = sel;
24124            
24125            
24126             Roo.menu.MenuMgr.hideAll();
24127
24128         }
24129         
24130         if (!updateFooter) {
24131             //this.footDisp.dom.innerHTML = ''; 
24132             return;
24133         }
24134         // update the footer
24135         //
24136         var html = '';
24137         
24138         this.footerEls = ans.reverse();
24139         Roo.each(this.footerEls, function(a,i) {
24140             if (!a) { return; }
24141             html += html.length ? ' &gt; '  :  '';
24142             
24143             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24144             
24145         });
24146        
24147         // 
24148         var sz = this.footDisp.up('td').getSize();
24149         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24150         this.footDisp.dom.style.marginLeft = '5px';
24151         
24152         this.footDisp.dom.style.overflow = 'hidden';
24153         
24154         this.footDisp.dom.innerHTML = html;
24155             
24156         //this.editorsyncValue();
24157     },
24158      
24159     
24160    
24161        
24162     // private
24163     onDestroy : function(){
24164         if(this.rendered){
24165             
24166             this.tb.items.each(function(item){
24167                 if(item.menu){
24168                     item.menu.removeAll();
24169                     if(item.menu.el){
24170                         item.menu.el.destroy();
24171                     }
24172                 }
24173                 item.destroy();
24174             });
24175              
24176         }
24177     },
24178     onFirstFocus: function() {
24179         // need to do this for all the toolbars..
24180         this.tb.items.each(function(item){
24181            item.enable();
24182         });
24183     },
24184     buildToolbar: function(tlist, nm)
24185     {
24186         var editor = this.editor;
24187         var editorcore = this.editorcore;
24188          // create a new element.
24189         var wdiv = editor.wrap.createChild({
24190                 tag: 'div'
24191             }, editor.wrap.dom.firstChild.nextSibling, true);
24192         
24193        
24194         var tb = new Roo.Toolbar(wdiv);
24195         // add the name..
24196         
24197         tb.add(nm+ ":&nbsp;");
24198         
24199         var styles = [];
24200         for(var i in this.styles) {
24201             styles.push(i);
24202         }
24203         
24204         // styles...
24205         if (styles && styles.length) {
24206             
24207             // this needs a multi-select checkbox...
24208             tb.addField( new Roo.form.ComboBox({
24209                 store: new Roo.data.SimpleStore({
24210                     id : 'val',
24211                     fields: ['val', 'selected'],
24212                     data : [] 
24213                 }),
24214                 name : '-roo-edit-className',
24215                 attrname : 'className',
24216                 displayField: 'val',
24217                 typeAhead: false,
24218                 mode: 'local',
24219                 editable : false,
24220                 triggerAction: 'all',
24221                 emptyText:'Select Style',
24222                 selectOnFocus:true,
24223                 width: 130,
24224                 listeners : {
24225                     'select': function(c, r, i) {
24226                         // initial support only for on class per el..
24227                         tb.selectedNode.className =  r ? r.get('val') : '';
24228                         editorcore.syncValue();
24229                     }
24230                 }
24231     
24232             }));
24233         }
24234         
24235         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24236         var tbops = tbc.options;
24237         
24238         for (var i in tlist) {
24239             
24240             var item = tlist[i];
24241             tb.add(item.title + ":&nbsp;");
24242             
24243             
24244             //optname == used so you can configure the options available..
24245             var opts = item.opts ? item.opts : false;
24246             if (item.optname) {
24247                 opts = tbops[item.optname];
24248            
24249             }
24250             
24251             if (opts) {
24252                 // opts == pulldown..
24253                 tb.addField( new Roo.form.ComboBox({
24254                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24255                         id : 'val',
24256                         fields: ['val', 'display'],
24257                         data : opts  
24258                     }),
24259                     name : '-roo-edit-' + i,
24260                     attrname : i,
24261                     stylename : item.style ? item.style : false,
24262                     displayField: item.displayField ? item.displayField : 'val',
24263                     valueField :  'val',
24264                     typeAhead: false,
24265                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24266                     editable : false,
24267                     triggerAction: 'all',
24268                     emptyText:'Select',
24269                     selectOnFocus:true,
24270                     width: item.width ? item.width  : 130,
24271                     listeners : {
24272                         'select': function(c, r, i) {
24273                             if (c.stylename) {
24274                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24275                                 return;
24276                             }
24277                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24278                         }
24279                     }
24280
24281                 }));
24282                 continue;
24283                     
24284                  
24285                 
24286                 tb.addField( new Roo.form.TextField({
24287                     name: i,
24288                     width: 100,
24289                     //allowBlank:false,
24290                     value: ''
24291                 }));
24292                 continue;
24293             }
24294             tb.addField( new Roo.form.TextField({
24295                 name: '-roo-edit-' + i,
24296                 attrname : i,
24297                 
24298                 width: item.width,
24299                 //allowBlank:true,
24300                 value: '',
24301                 listeners: {
24302                     'change' : function(f, nv, ov) {
24303                         tb.selectedNode.setAttribute(f.attrname, nv);
24304                         editorcore.syncValue();
24305                     }
24306                 }
24307             }));
24308              
24309         }
24310         
24311         var _this = this;
24312         
24313         if(nm == 'BODY'){
24314             tb.addSeparator();
24315         
24316             tb.addButton( {
24317                 text: 'Stylesheets',
24318
24319                 listeners : {
24320                     click : function ()
24321                     {
24322                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24323                     }
24324                 }
24325             });
24326         }
24327         
24328         tb.addFill();
24329         tb.addButton( {
24330             text: 'Remove Tag',
24331     
24332             listeners : {
24333                 click : function ()
24334                 {
24335                     // remove
24336                     // undo does not work.
24337                      
24338                     var sn = tb.selectedNode;
24339                     
24340                     var pn = sn.parentNode;
24341                     
24342                     var stn =  sn.childNodes[0];
24343                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24344                     while (sn.childNodes.length) {
24345                         var node = sn.childNodes[0];
24346                         sn.removeChild(node);
24347                         //Roo.log(node);
24348                         pn.insertBefore(node, sn);
24349                         
24350                     }
24351                     pn.removeChild(sn);
24352                     var range = editorcore.createRange();
24353         
24354                     range.setStart(stn,0);
24355                     range.setEnd(en,0); //????
24356                     //range.selectNode(sel);
24357                     
24358                     
24359                     var selection = editorcore.getSelection();
24360                     selection.removeAllRanges();
24361                     selection.addRange(range);
24362                     
24363                     
24364                     
24365                     //_this.updateToolbar(null, null, pn);
24366                     _this.updateToolbar(null, null, null);
24367                     _this.footDisp.dom.innerHTML = ''; 
24368                 }
24369             }
24370             
24371                     
24372                 
24373             
24374         });
24375         
24376         
24377         tb.el.on('click', function(e){
24378             e.preventDefault(); // what does this do?
24379         });
24380         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24381         tb.el.hide();
24382         tb.name = nm;
24383         // dont need to disable them... as they will get hidden
24384         return tb;
24385          
24386         
24387     },
24388     buildFooter : function()
24389     {
24390         
24391         var fel = this.editor.wrap.createChild();
24392         this.footer = new Roo.Toolbar(fel);
24393         // toolbar has scrolly on left / right?
24394         var footDisp= new Roo.Toolbar.Fill();
24395         var _t = this;
24396         this.footer.add(
24397             {
24398                 text : '&lt;',
24399                 xtype: 'Button',
24400                 handler : function() {
24401                     _t.footDisp.scrollTo('left',0,true)
24402                 }
24403             }
24404         );
24405         this.footer.add( footDisp );
24406         this.footer.add( 
24407             {
24408                 text : '&gt;',
24409                 xtype: 'Button',
24410                 handler : function() {
24411                     // no animation..
24412                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24413                 }
24414             }
24415         );
24416         var fel = Roo.get(footDisp.el);
24417         fel.addClass('x-editor-context');
24418         this.footDispWrap = fel; 
24419         this.footDispWrap.overflow  = 'hidden';
24420         
24421         this.footDisp = fel.createChild();
24422         this.footDispWrap.on('click', this.onContextClick, this)
24423         
24424         
24425     },
24426     onContextClick : function (ev,dom)
24427     {
24428         ev.preventDefault();
24429         var  cn = dom.className;
24430         //Roo.log(cn);
24431         if (!cn.match(/x-ed-loc-/)) {
24432             return;
24433         }
24434         var n = cn.split('-').pop();
24435         var ans = this.footerEls;
24436         var sel = ans[n];
24437         
24438          // pick
24439         var range = this.editorcore.createRange();
24440         
24441         range.selectNodeContents(sel);
24442         //range.selectNode(sel);
24443         
24444         
24445         var selection = this.editorcore.getSelection();
24446         selection.removeAllRanges();
24447         selection.addRange(range);
24448         
24449         
24450         
24451         this.updateToolbar(null, null, sel);
24452         
24453         
24454     }
24455     
24456     
24457     
24458     
24459     
24460 });
24461
24462
24463
24464
24465
24466 /*
24467  * Based on:
24468  * Ext JS Library 1.1.1
24469  * Copyright(c) 2006-2007, Ext JS, LLC.
24470  *
24471  * Originally Released Under LGPL - original licence link has changed is not relivant.
24472  *
24473  * Fork - LGPL
24474  * <script type="text/javascript">
24475  */
24476  
24477 /**
24478  * @class Roo.form.BasicForm
24479  * @extends Roo.util.Observable
24480  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24481  * @constructor
24482  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24483  * @param {Object} config Configuration options
24484  */
24485 Roo.form.BasicForm = function(el, config){
24486     this.allItems = [];
24487     this.childForms = [];
24488     Roo.apply(this, config);
24489     /*
24490      * The Roo.form.Field items in this form.
24491      * @type MixedCollection
24492      */
24493      
24494      
24495     this.items = new Roo.util.MixedCollection(false, function(o){
24496         return o.id || (o.id = Roo.id());
24497     });
24498     this.addEvents({
24499         /**
24500          * @event beforeaction
24501          * Fires before any action is performed. Return false to cancel the action.
24502          * @param {Form} this
24503          * @param {Action} action The action to be performed
24504          */
24505         beforeaction: true,
24506         /**
24507          * @event actionfailed
24508          * Fires when an action fails.
24509          * @param {Form} this
24510          * @param {Action} action The action that failed
24511          */
24512         actionfailed : true,
24513         /**
24514          * @event actioncomplete
24515          * Fires when an action is completed.
24516          * @param {Form} this
24517          * @param {Action} action The action that completed
24518          */
24519         actioncomplete : true
24520     });
24521     if(el){
24522         this.initEl(el);
24523     }
24524     Roo.form.BasicForm.superclass.constructor.call(this);
24525     
24526     Roo.form.BasicForm.popover.apply();
24527 };
24528
24529 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24530     /**
24531      * @cfg {String} method
24532      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24533      */
24534     /**
24535      * @cfg {DataReader} reader
24536      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24537      * This is optional as there is built-in support for processing JSON.
24538      */
24539     /**
24540      * @cfg {DataReader} errorReader
24541      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24542      * This is completely optional as there is built-in support for processing JSON.
24543      */
24544     /**
24545      * @cfg {String} url
24546      * The URL to use for form actions if one isn't supplied in the action options.
24547      */
24548     /**
24549      * @cfg {Boolean} fileUpload
24550      * Set to true if this form is a file upload.
24551      */
24552      
24553     /**
24554      * @cfg {Object} baseParams
24555      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24556      */
24557      /**
24558      
24559     /**
24560      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24561      */
24562     timeout: 30,
24563
24564     // private
24565     activeAction : null,
24566
24567     /**
24568      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24569      * or setValues() data instead of when the form was first created.
24570      */
24571     trackResetOnLoad : false,
24572     
24573     
24574     /**
24575      * childForms - used for multi-tab forms
24576      * @type {Array}
24577      */
24578     childForms : false,
24579     
24580     /**
24581      * allItems - full list of fields.
24582      * @type {Array}
24583      */
24584     allItems : false,
24585     
24586     /**
24587      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24588      * element by passing it or its id or mask the form itself by passing in true.
24589      * @type Mixed
24590      */
24591     waitMsgTarget : false,
24592     
24593     /**
24594      * @type Boolean
24595      */
24596     disableMask : false,
24597     
24598     /**
24599      * @cfg {Boolean} errorMask (true|false) default false
24600      */
24601     errorMask : false,
24602     
24603     /**
24604      * @cfg {Number} maskOffset Default 100
24605      */
24606     maskOffset : 100,
24607
24608     // private
24609     initEl : function(el){
24610         this.el = Roo.get(el);
24611         this.id = this.el.id || Roo.id();
24612         this.el.on('submit', this.onSubmit, this);
24613         this.el.addClass('x-form');
24614     },
24615
24616     // private
24617     onSubmit : function(e){
24618         e.stopEvent();
24619     },
24620
24621     /**
24622      * Returns true if client-side validation on the form is successful.
24623      * @return Boolean
24624      */
24625     isValid : function(){
24626         var valid = true;
24627         var target = false;
24628         this.items.each(function(f){
24629             if(f.validate()){
24630                 return;
24631             }
24632             
24633             valid = false;
24634                 
24635             if(!target && f.el.isVisible(true)){
24636                 target = f;
24637             }
24638         });
24639         
24640         if(this.errorMask && !valid){
24641             Roo.form.BasicForm.popover.mask(this, target);
24642         }
24643         
24644         return valid;
24645     },
24646     /**
24647      * Returns array of invalid form fields.
24648      * @return Array
24649      */
24650     
24651     invalidFields : function()
24652     {
24653         var ret = [];
24654         this.items.each(function(f){
24655             if(f.validate()){
24656                 return;
24657             }
24658             ret.push(f);
24659             
24660         });
24661         
24662         return ret;
24663     },
24664     
24665     
24666     /**
24667      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24668      * @return Boolean
24669      */
24670     isDirty : function(){
24671         var dirty = false;
24672         this.items.each(function(f){
24673            if(f.isDirty()){
24674                dirty = true;
24675                return false;
24676            }
24677         });
24678         return dirty;
24679     },
24680     
24681     /**
24682      * Returns true if any fields in this form have changed since their original load. (New version)
24683      * @return Boolean
24684      */
24685     
24686     hasChanged : function()
24687     {
24688         var dirty = false;
24689         this.items.each(function(f){
24690            if(f.hasChanged()){
24691                dirty = true;
24692                return false;
24693            }
24694         });
24695         return dirty;
24696         
24697     },
24698     /**
24699      * Resets all hasChanged to 'false' -
24700      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24701      * So hasChanged storage is only to be used for this purpose
24702      * @return Boolean
24703      */
24704     resetHasChanged : function()
24705     {
24706         this.items.each(function(f){
24707            f.resetHasChanged();
24708         });
24709         
24710     },
24711     
24712     
24713     /**
24714      * Performs a predefined action (submit or load) or custom actions you define on this form.
24715      * @param {String} actionName The name of the action type
24716      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24717      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24718      * accept other config options):
24719      * <pre>
24720 Property          Type             Description
24721 ----------------  ---------------  ----------------------------------------------------------------------------------
24722 url               String           The url for the action (defaults to the form's url)
24723 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24724 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24725 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24726                                    validate the form on the client (defaults to false)
24727      * </pre>
24728      * @return {BasicForm} this
24729      */
24730     doAction : function(action, options){
24731         if(typeof action == 'string'){
24732             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24733         }
24734         if(this.fireEvent('beforeaction', this, action) !== false){
24735             this.beforeAction(action);
24736             action.run.defer(100, action);
24737         }
24738         return this;
24739     },
24740
24741     /**
24742      * Shortcut to do a submit action.
24743      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24744      * @return {BasicForm} this
24745      */
24746     submit : function(options){
24747         this.doAction('submit', options);
24748         return this;
24749     },
24750
24751     /**
24752      * Shortcut to do a load action.
24753      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24754      * @return {BasicForm} this
24755      */
24756     load : function(options){
24757         this.doAction('load', options);
24758         return this;
24759     },
24760
24761     /**
24762      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24763      * @param {Record} record The record to edit
24764      * @return {BasicForm} this
24765      */
24766     updateRecord : function(record){
24767         record.beginEdit();
24768         var fs = record.fields;
24769         fs.each(function(f){
24770             var field = this.findField(f.name);
24771             if(field){
24772                 record.set(f.name, field.getValue());
24773             }
24774         }, this);
24775         record.endEdit();
24776         return this;
24777     },
24778
24779     /**
24780      * Loads an Roo.data.Record into this form.
24781      * @param {Record} record The record to load
24782      * @return {BasicForm} this
24783      */
24784     loadRecord : function(record){
24785         this.setValues(record.data);
24786         return this;
24787     },
24788
24789     // private
24790     beforeAction : function(action){
24791         var o = action.options;
24792         
24793         if(!this.disableMask) {
24794             if(this.waitMsgTarget === true){
24795                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24796             }else if(this.waitMsgTarget){
24797                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24798                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24799             }else {
24800                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24801             }
24802         }
24803         
24804          
24805     },
24806
24807     // private
24808     afterAction : function(action, success){
24809         this.activeAction = null;
24810         var o = action.options;
24811         
24812         if(!this.disableMask) {
24813             if(this.waitMsgTarget === true){
24814                 this.el.unmask();
24815             }else if(this.waitMsgTarget){
24816                 this.waitMsgTarget.unmask();
24817             }else{
24818                 Roo.MessageBox.updateProgress(1);
24819                 Roo.MessageBox.hide();
24820             }
24821         }
24822         
24823         if(success){
24824             if(o.reset){
24825                 this.reset();
24826             }
24827             Roo.callback(o.success, o.scope, [this, action]);
24828             this.fireEvent('actioncomplete', this, action);
24829             
24830         }else{
24831             
24832             // failure condition..
24833             // we have a scenario where updates need confirming.
24834             // eg. if a locking scenario exists..
24835             // we look for { errors : { needs_confirm : true }} in the response.
24836             if (
24837                 (typeof(action.result) != 'undefined')  &&
24838                 (typeof(action.result.errors) != 'undefined')  &&
24839                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24840            ){
24841                 var _t = this;
24842                 Roo.MessageBox.confirm(
24843                     "Change requires confirmation",
24844                     action.result.errorMsg,
24845                     function(r) {
24846                         if (r != 'yes') {
24847                             return;
24848                         }
24849                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24850                     }
24851                     
24852                 );
24853                 
24854                 
24855                 
24856                 return;
24857             }
24858             
24859             Roo.callback(o.failure, o.scope, [this, action]);
24860             // show an error message if no failed handler is set..
24861             if (!this.hasListener('actionfailed')) {
24862                 Roo.MessageBox.alert("Error",
24863                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24864                         action.result.errorMsg :
24865                         "Saving Failed, please check your entries or try again"
24866                 );
24867             }
24868             
24869             this.fireEvent('actionfailed', this, action);
24870         }
24871         
24872     },
24873
24874     /**
24875      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24876      * @param {String} id The value to search for
24877      * @return Field
24878      */
24879     findField : function(id){
24880         var field = this.items.get(id);
24881         if(!field){
24882             this.items.each(function(f){
24883                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24884                     field = f;
24885                     return false;
24886                 }
24887             });
24888         }
24889         return field || null;
24890     },
24891
24892     /**
24893      * Add a secondary form to this one, 
24894      * Used to provide tabbed forms. One form is primary, with hidden values 
24895      * which mirror the elements from the other forms.
24896      * 
24897      * @param {Roo.form.Form} form to add.
24898      * 
24899      */
24900     addForm : function(form)
24901     {
24902        
24903         if (this.childForms.indexOf(form) > -1) {
24904             // already added..
24905             return;
24906         }
24907         this.childForms.push(form);
24908         var n = '';
24909         Roo.each(form.allItems, function (fe) {
24910             
24911             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24912             if (this.findField(n)) { // already added..
24913                 return;
24914             }
24915             var add = new Roo.form.Hidden({
24916                 name : n
24917             });
24918             add.render(this.el);
24919             
24920             this.add( add );
24921         }, this);
24922         
24923     },
24924     /**
24925      * Mark fields in this form invalid in bulk.
24926      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24927      * @return {BasicForm} this
24928      */
24929     markInvalid : function(errors){
24930         if(errors instanceof Array){
24931             for(var i = 0, len = errors.length; i < len; i++){
24932                 var fieldError = errors[i];
24933                 var f = this.findField(fieldError.id);
24934                 if(f){
24935                     f.markInvalid(fieldError.msg);
24936                 }
24937             }
24938         }else{
24939             var field, id;
24940             for(id in errors){
24941                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24942                     field.markInvalid(errors[id]);
24943                 }
24944             }
24945         }
24946         Roo.each(this.childForms || [], function (f) {
24947             f.markInvalid(errors);
24948         });
24949         
24950         return this;
24951     },
24952
24953     /**
24954      * Set values for fields in this form in bulk.
24955      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24956      * @return {BasicForm} this
24957      */
24958     setValues : function(values){
24959         if(values instanceof Array){ // array of objects
24960             for(var i = 0, len = values.length; i < len; i++){
24961                 var v = values[i];
24962                 var f = this.findField(v.id);
24963                 if(f){
24964                     f.setValue(v.value);
24965                     if(this.trackResetOnLoad){
24966                         f.originalValue = f.getValue();
24967                     }
24968                 }
24969             }
24970         }else{ // object hash
24971             var field, id;
24972             for(id in values){
24973                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24974                     
24975                     if (field.setFromData && 
24976                         field.valueField && 
24977                         field.displayField &&
24978                         // combos' with local stores can 
24979                         // be queried via setValue()
24980                         // to set their value..
24981                         (field.store && !field.store.isLocal)
24982                         ) {
24983                         // it's a combo
24984                         var sd = { };
24985                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24986                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24987                         field.setFromData(sd);
24988                         
24989                     } else {
24990                         field.setValue(values[id]);
24991                     }
24992                     
24993                     
24994                     if(this.trackResetOnLoad){
24995                         field.originalValue = field.getValue();
24996                     }
24997                 }
24998             }
24999         }
25000         this.resetHasChanged();
25001         
25002         
25003         Roo.each(this.childForms || [], function (f) {
25004             f.setValues(values);
25005             f.resetHasChanged();
25006         });
25007                 
25008         return this;
25009     },
25010  
25011     /**
25012      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25013      * they are returned as an array.
25014      * @param {Boolean} asString
25015      * @return {Object}
25016      */
25017     getValues : function(asString){
25018         if (this.childForms) {
25019             // copy values from the child forms
25020             Roo.each(this.childForms, function (f) {
25021                 this.setValues(f.getValues());
25022             }, this);
25023         }
25024         
25025         // use formdata
25026         if (typeof(FormData) != 'undefined' && asString !== true) {
25027             // this relies on a 'recent' version of chrome apparently...
25028             try {
25029                 var fd = (new FormData(this.el.dom)).entries();
25030                 var ret = {};
25031                 var ent = fd.next();
25032                 while (!ent.done) {
25033                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25034                     ent = fd.next();
25035                 };
25036                 return ret;
25037             } catch(e) {
25038                 
25039             }
25040             
25041         }
25042         
25043         
25044         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25045         if(asString === true){
25046             return fs;
25047         }
25048         return Roo.urlDecode(fs);
25049     },
25050     
25051     /**
25052      * Returns the fields in this form as an object with key/value pairs. 
25053      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25054      * @return {Object}
25055      */
25056     getFieldValues : function(with_hidden)
25057     {
25058         if (this.childForms) {
25059             // copy values from the child forms
25060             // should this call getFieldValues - probably not as we do not currently copy
25061             // hidden fields when we generate..
25062             Roo.each(this.childForms, function (f) {
25063                 this.setValues(f.getValues());
25064             }, this);
25065         }
25066         
25067         var ret = {};
25068         this.items.each(function(f){
25069             if (!f.getName()) {
25070                 return;
25071             }
25072             var v = f.getValue();
25073             if (f.inputType =='radio') {
25074                 if (typeof(ret[f.getName()]) == 'undefined') {
25075                     ret[f.getName()] = ''; // empty..
25076                 }
25077                 
25078                 if (!f.el.dom.checked) {
25079                     return;
25080                     
25081                 }
25082                 v = f.el.dom.value;
25083                 
25084             }
25085             
25086             // not sure if this supported any more..
25087             if ((typeof(v) == 'object') && f.getRawValue) {
25088                 v = f.getRawValue() ; // dates..
25089             }
25090             // combo boxes where name != hiddenName...
25091             if (f.name != f.getName()) {
25092                 ret[f.name] = f.getRawValue();
25093             }
25094             ret[f.getName()] = v;
25095         });
25096         
25097         return ret;
25098     },
25099
25100     /**
25101      * Clears all invalid messages in this form.
25102      * @return {BasicForm} this
25103      */
25104     clearInvalid : function(){
25105         this.items.each(function(f){
25106            f.clearInvalid();
25107         });
25108         
25109         Roo.each(this.childForms || [], function (f) {
25110             f.clearInvalid();
25111         });
25112         
25113         
25114         return this;
25115     },
25116
25117     /**
25118      * Resets this form.
25119      * @return {BasicForm} this
25120      */
25121     reset : function(){
25122         this.items.each(function(f){
25123             f.reset();
25124         });
25125         
25126         Roo.each(this.childForms || [], function (f) {
25127             f.reset();
25128         });
25129         this.resetHasChanged();
25130         
25131         return this;
25132     },
25133
25134     /**
25135      * Add Roo.form components to this form.
25136      * @param {Field} field1
25137      * @param {Field} field2 (optional)
25138      * @param {Field} etc (optional)
25139      * @return {BasicForm} this
25140      */
25141     add : function(){
25142         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25143         return this;
25144     },
25145
25146
25147     /**
25148      * Removes a field from the items collection (does NOT remove its markup).
25149      * @param {Field} field
25150      * @return {BasicForm} this
25151      */
25152     remove : function(field){
25153         this.items.remove(field);
25154         return this;
25155     },
25156
25157     /**
25158      * Looks at the fields in this form, checks them for an id attribute,
25159      * and calls applyTo on the existing dom element with that id.
25160      * @return {BasicForm} this
25161      */
25162     render : function(){
25163         this.items.each(function(f){
25164             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25165                 f.applyTo(f.id);
25166             }
25167         });
25168         return this;
25169     },
25170
25171     /**
25172      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25173      * @param {Object} values
25174      * @return {BasicForm} this
25175      */
25176     applyToFields : function(o){
25177         this.items.each(function(f){
25178            Roo.apply(f, o);
25179         });
25180         return this;
25181     },
25182
25183     /**
25184      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25185      * @param {Object} values
25186      * @return {BasicForm} this
25187      */
25188     applyIfToFields : function(o){
25189         this.items.each(function(f){
25190            Roo.applyIf(f, o);
25191         });
25192         return this;
25193     }
25194 });
25195
25196 // back compat
25197 Roo.BasicForm = Roo.form.BasicForm;
25198
25199 Roo.apply(Roo.form.BasicForm, {
25200     
25201     popover : {
25202         
25203         padding : 5,
25204         
25205         isApplied : false,
25206         
25207         isMasked : false,
25208         
25209         form : false,
25210         
25211         target : false,
25212         
25213         intervalID : false,
25214         
25215         maskEl : false,
25216         
25217         apply : function()
25218         {
25219             if(this.isApplied){
25220                 return;
25221             }
25222             
25223             this.maskEl = {
25224                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25225                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25226                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25227                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25228             };
25229             
25230             this.maskEl.top.enableDisplayMode("block");
25231             this.maskEl.left.enableDisplayMode("block");
25232             this.maskEl.bottom.enableDisplayMode("block");
25233             this.maskEl.right.enableDisplayMode("block");
25234             
25235             Roo.get(document.body).on('click', function(){
25236                 this.unmask();
25237             }, this);
25238             
25239             Roo.get(document.body).on('touchstart', function(){
25240                 this.unmask();
25241             }, this);
25242             
25243             this.isApplied = true
25244         },
25245         
25246         mask : function(form, target)
25247         {
25248             this.form = form;
25249             
25250             this.target = target;
25251             
25252             if(!this.form.errorMask || !target.el){
25253                 return;
25254             }
25255             
25256             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25257             
25258             var ot = this.target.el.calcOffsetsTo(scrollable);
25259             
25260             var scrollTo = ot[1] - this.form.maskOffset;
25261             
25262             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25263             
25264             scrollable.scrollTo('top', scrollTo);
25265             
25266             var el = this.target.wrap || this.target.el;
25267             
25268             var box = el.getBox();
25269             
25270             this.maskEl.top.setStyle('position', 'absolute');
25271             this.maskEl.top.setStyle('z-index', 10000);
25272             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25273             this.maskEl.top.setLeft(0);
25274             this.maskEl.top.setTop(0);
25275             this.maskEl.top.show();
25276             
25277             this.maskEl.left.setStyle('position', 'absolute');
25278             this.maskEl.left.setStyle('z-index', 10000);
25279             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25280             this.maskEl.left.setLeft(0);
25281             this.maskEl.left.setTop(box.y - this.padding);
25282             this.maskEl.left.show();
25283
25284             this.maskEl.bottom.setStyle('position', 'absolute');
25285             this.maskEl.bottom.setStyle('z-index', 10000);
25286             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25287             this.maskEl.bottom.setLeft(0);
25288             this.maskEl.bottom.setTop(box.bottom + this.padding);
25289             this.maskEl.bottom.show();
25290
25291             this.maskEl.right.setStyle('position', 'absolute');
25292             this.maskEl.right.setStyle('z-index', 10000);
25293             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25294             this.maskEl.right.setLeft(box.right + this.padding);
25295             this.maskEl.right.setTop(box.y - this.padding);
25296             this.maskEl.right.show();
25297
25298             this.intervalID = window.setInterval(function() {
25299                 Roo.form.BasicForm.popover.unmask();
25300             }, 10000);
25301
25302             window.onwheel = function(){ return false;};
25303             
25304             (function(){ this.isMasked = true; }).defer(500, this);
25305             
25306         },
25307         
25308         unmask : function()
25309         {
25310             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25311                 return;
25312             }
25313             
25314             this.maskEl.top.setStyle('position', 'absolute');
25315             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25316             this.maskEl.top.hide();
25317
25318             this.maskEl.left.setStyle('position', 'absolute');
25319             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25320             this.maskEl.left.hide();
25321
25322             this.maskEl.bottom.setStyle('position', 'absolute');
25323             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25324             this.maskEl.bottom.hide();
25325
25326             this.maskEl.right.setStyle('position', 'absolute');
25327             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25328             this.maskEl.right.hide();
25329             
25330             window.onwheel = function(){ return true;};
25331             
25332             if(this.intervalID){
25333                 window.clearInterval(this.intervalID);
25334                 this.intervalID = false;
25335             }
25336             
25337             this.isMasked = false;
25338             
25339         }
25340         
25341     }
25342     
25343 });/*
25344  * Based on:
25345  * Ext JS Library 1.1.1
25346  * Copyright(c) 2006-2007, Ext JS, LLC.
25347  *
25348  * Originally Released Under LGPL - original licence link has changed is not relivant.
25349  *
25350  * Fork - LGPL
25351  * <script type="text/javascript">
25352  */
25353
25354 /**
25355  * @class Roo.form.Form
25356  * @extends Roo.form.BasicForm
25357  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25358  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25359  * @constructor
25360  * @param {Object} config Configuration options
25361  */
25362 Roo.form.Form = function(config){
25363     var xitems =  [];
25364     if (config.items) {
25365         xitems = config.items;
25366         delete config.items;
25367     }
25368    
25369     
25370     Roo.form.Form.superclass.constructor.call(this, null, config);
25371     this.url = this.url || this.action;
25372     if(!this.root){
25373         this.root = new Roo.form.Layout(Roo.applyIf({
25374             id: Roo.id()
25375         }, config));
25376     }
25377     this.active = this.root;
25378     /**
25379      * Array of all the buttons that have been added to this form via {@link addButton}
25380      * @type Array
25381      */
25382     this.buttons = [];
25383     this.allItems = [];
25384     this.addEvents({
25385         /**
25386          * @event clientvalidation
25387          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25388          * @param {Form} this
25389          * @param {Boolean} valid true if the form has passed client-side validation
25390          */
25391         clientvalidation: true,
25392         /**
25393          * @event rendered
25394          * Fires when the form is rendered
25395          * @param {Roo.form.Form} form
25396          */
25397         rendered : true
25398     });
25399     
25400     if (this.progressUrl) {
25401             // push a hidden field onto the list of fields..
25402             this.addxtype( {
25403                     xns: Roo.form, 
25404                     xtype : 'Hidden', 
25405                     name : 'UPLOAD_IDENTIFIER' 
25406             });
25407         }
25408         
25409     
25410     Roo.each(xitems, this.addxtype, this);
25411     
25412 };
25413
25414 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25415      /**
25416      * @cfg {Roo.Button} buttons[] buttons at bottom of form
25417      */
25418     
25419     /**
25420      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25421      */
25422     /**
25423      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25424      */
25425     /**
25426      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25427      */
25428     buttonAlign:'center',
25429
25430     /**
25431      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25432      */
25433     minButtonWidth:75,
25434
25435     /**
25436      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25437      * This property cascades to child containers if not set.
25438      */
25439     labelAlign:'left',
25440
25441     /**
25442      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25443      * fires a looping event with that state. This is required to bind buttons to the valid
25444      * state using the config value formBind:true on the button.
25445      */
25446     monitorValid : false,
25447
25448     /**
25449      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25450      */
25451     monitorPoll : 200,
25452     
25453     /**
25454      * @cfg {String} progressUrl - Url to return progress data 
25455      */
25456     
25457     progressUrl : false,
25458     /**
25459      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25460      * sending a formdata with extra parameters - eg uploaded elements.
25461      */
25462     
25463     formData : false,
25464     
25465     /**
25466      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25467      * fields are added and the column is closed. If no fields are passed the column remains open
25468      * until end() is called.
25469      * @param {Object} config The config to pass to the column
25470      * @param {Field} field1 (optional)
25471      * @param {Field} field2 (optional)
25472      * @param {Field} etc (optional)
25473      * @return Column The column container object
25474      */
25475     column : function(c){
25476         var col = new Roo.form.Column(c);
25477         this.start(col);
25478         if(arguments.length > 1){ // duplicate code required because of Opera
25479             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25480             this.end();
25481         }
25482         return col;
25483     },
25484
25485     /**
25486      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25487      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25488      * until end() is called.
25489      * @param {Object} config The config to pass to the fieldset
25490      * @param {Field} field1 (optional)
25491      * @param {Field} field2 (optional)
25492      * @param {Field} etc (optional)
25493      * @return FieldSet The fieldset container object
25494      */
25495     fieldset : function(c){
25496         var fs = new Roo.form.FieldSet(c);
25497         this.start(fs);
25498         if(arguments.length > 1){ // duplicate code required because of Opera
25499             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25500             this.end();
25501         }
25502         return fs;
25503     },
25504
25505     /**
25506      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25507      * fields are added and the container is closed. If no fields are passed the container remains open
25508      * until end() is called.
25509      * @param {Object} config The config to pass to the Layout
25510      * @param {Field} field1 (optional)
25511      * @param {Field} field2 (optional)
25512      * @param {Field} etc (optional)
25513      * @return Layout The container object
25514      */
25515     container : function(c){
25516         var l = new Roo.form.Layout(c);
25517         this.start(l);
25518         if(arguments.length > 1){ // duplicate code required because of Opera
25519             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25520             this.end();
25521         }
25522         return l;
25523     },
25524
25525     /**
25526      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25527      * @param {Object} container A Roo.form.Layout or subclass of Layout
25528      * @return {Form} this
25529      */
25530     start : function(c){
25531         // cascade label info
25532         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25533         this.active.stack.push(c);
25534         c.ownerCt = this.active;
25535         this.active = c;
25536         return this;
25537     },
25538
25539     /**
25540      * Closes the current open container
25541      * @return {Form} this
25542      */
25543     end : function(){
25544         if(this.active == this.root){
25545             return this;
25546         }
25547         this.active = this.active.ownerCt;
25548         return this;
25549     },
25550
25551     /**
25552      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25553      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25554      * as the label of the field.
25555      * @param {Field} field1
25556      * @param {Field} field2 (optional)
25557      * @param {Field} etc. (optional)
25558      * @return {Form} this
25559      */
25560     add : function(){
25561         this.active.stack.push.apply(this.active.stack, arguments);
25562         this.allItems.push.apply(this.allItems,arguments);
25563         var r = [];
25564         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25565             if(a[i].isFormField){
25566                 r.push(a[i]);
25567             }
25568         }
25569         if(r.length > 0){
25570             Roo.form.Form.superclass.add.apply(this, r);
25571         }
25572         return this;
25573     },
25574     
25575
25576     
25577     
25578     
25579      /**
25580      * Find any element that has been added to a form, using it's ID or name
25581      * This can include framesets, columns etc. along with regular fields..
25582      * @param {String} id - id or name to find.
25583      
25584      * @return {Element} e - or false if nothing found.
25585      */
25586     findbyId : function(id)
25587     {
25588         var ret = false;
25589         if (!id) {
25590             return ret;
25591         }
25592         Roo.each(this.allItems, function(f){
25593             if (f.id == id || f.name == id ){
25594                 ret = f;
25595                 return false;
25596             }
25597         });
25598         return ret;
25599     },
25600
25601     
25602     
25603     /**
25604      * Render this form into the passed container. This should only be called once!
25605      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25606      * @return {Form} this
25607      */
25608     render : function(ct)
25609     {
25610         
25611         
25612         
25613         ct = Roo.get(ct);
25614         var o = this.autoCreate || {
25615             tag: 'form',
25616             method : this.method || 'POST',
25617             id : this.id || Roo.id()
25618         };
25619         this.initEl(ct.createChild(o));
25620
25621         this.root.render(this.el);
25622         
25623        
25624              
25625         this.items.each(function(f){
25626             f.render('x-form-el-'+f.id);
25627         });
25628
25629         if(this.buttons.length > 0){
25630             // tables are required to maintain order and for correct IE layout
25631             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25632                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25633                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25634             }}, null, true);
25635             var tr = tb.getElementsByTagName('tr')[0];
25636             for(var i = 0, len = this.buttons.length; i < len; i++) {
25637                 var b = this.buttons[i];
25638                 var td = document.createElement('td');
25639                 td.className = 'x-form-btn-td';
25640                 b.render(tr.appendChild(td));
25641             }
25642         }
25643         if(this.monitorValid){ // initialize after render
25644             this.startMonitoring();
25645         }
25646         this.fireEvent('rendered', this);
25647         return this;
25648     },
25649
25650     /**
25651      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25652      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25653      * object or a valid Roo.DomHelper element config
25654      * @param {Function} handler The function called when the button is clicked
25655      * @param {Object} scope (optional) The scope of the handler function
25656      * @return {Roo.Button}
25657      */
25658     addButton : function(config, handler, scope){
25659         var bc = {
25660             handler: handler,
25661             scope: scope,
25662             minWidth: this.minButtonWidth,
25663             hideParent:true
25664         };
25665         if(typeof config == "string"){
25666             bc.text = config;
25667         }else{
25668             Roo.apply(bc, config);
25669         }
25670         var btn = new Roo.Button(null, bc);
25671         this.buttons.push(btn);
25672         return btn;
25673     },
25674
25675      /**
25676      * Adds a series of form elements (using the xtype property as the factory method.
25677      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25678      * @param {Object} config 
25679      */
25680     
25681     addxtype : function()
25682     {
25683         var ar = Array.prototype.slice.call(arguments, 0);
25684         var ret = false;
25685         for(var i = 0; i < ar.length; i++) {
25686             if (!ar[i]) {
25687                 continue; // skip -- if this happends something invalid got sent, we 
25688                 // should ignore it, as basically that interface element will not show up
25689                 // and that should be pretty obvious!!
25690             }
25691             
25692             if (Roo.form[ar[i].xtype]) {
25693                 ar[i].form = this;
25694                 var fe = Roo.factory(ar[i], Roo.form);
25695                 if (!ret) {
25696                     ret = fe;
25697                 }
25698                 fe.form = this;
25699                 if (fe.store) {
25700                     fe.store.form = this;
25701                 }
25702                 if (fe.isLayout) {  
25703                          
25704                     this.start(fe);
25705                     this.allItems.push(fe);
25706                     if (fe.items && fe.addxtype) {
25707                         fe.addxtype.apply(fe, fe.items);
25708                         delete fe.items;
25709                     }
25710                      this.end();
25711                     continue;
25712                 }
25713                 
25714                 
25715                  
25716                 this.add(fe);
25717               //  console.log('adding ' + ar[i].xtype);
25718             }
25719             if (ar[i].xtype == 'Button') {  
25720                 //console.log('adding button');
25721                 //console.log(ar[i]);
25722                 this.addButton(ar[i]);
25723                 this.allItems.push(fe);
25724                 continue;
25725             }
25726             
25727             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25728                 alert('end is not supported on xtype any more, use items');
25729             //    this.end();
25730             //    //console.log('adding end');
25731             }
25732             
25733         }
25734         return ret;
25735     },
25736     
25737     /**
25738      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25739      * option "monitorValid"
25740      */
25741     startMonitoring : function(){
25742         if(!this.bound){
25743             this.bound = true;
25744             Roo.TaskMgr.start({
25745                 run : this.bindHandler,
25746                 interval : this.monitorPoll || 200,
25747                 scope: this
25748             });
25749         }
25750     },
25751
25752     /**
25753      * Stops monitoring of the valid state of this form
25754      */
25755     stopMonitoring : function(){
25756         this.bound = false;
25757     },
25758
25759     // private
25760     bindHandler : function(){
25761         if(!this.bound){
25762             return false; // stops binding
25763         }
25764         var valid = true;
25765         this.items.each(function(f){
25766             if(!f.isValid(true)){
25767                 valid = false;
25768                 return false;
25769             }
25770         });
25771         for(var i = 0, len = this.buttons.length; i < len; i++){
25772             var btn = this.buttons[i];
25773             if(btn.formBind === true && btn.disabled === valid){
25774                 btn.setDisabled(!valid);
25775             }
25776         }
25777         this.fireEvent('clientvalidation', this, valid);
25778     }
25779     
25780     
25781     
25782     
25783     
25784     
25785     
25786     
25787 });
25788
25789
25790 // back compat
25791 Roo.Form = Roo.form.Form;
25792 /*
25793  * Based on:
25794  * Ext JS Library 1.1.1
25795  * Copyright(c) 2006-2007, Ext JS, LLC.
25796  *
25797  * Originally Released Under LGPL - original licence link has changed is not relivant.
25798  *
25799  * Fork - LGPL
25800  * <script type="text/javascript">
25801  */
25802
25803 // as we use this in bootstrap.
25804 Roo.namespace('Roo.form');
25805  /**
25806  * @class Roo.form.Action
25807  * Internal Class used to handle form actions
25808  * @constructor
25809  * @param {Roo.form.BasicForm} el The form element or its id
25810  * @param {Object} config Configuration options
25811  */
25812
25813  
25814  
25815 // define the action interface
25816 Roo.form.Action = function(form, options){
25817     this.form = form;
25818     this.options = options || {};
25819 };
25820 /**
25821  * Client Validation Failed
25822  * @const 
25823  */
25824 Roo.form.Action.CLIENT_INVALID = 'client';
25825 /**
25826  * Server Validation Failed
25827  * @const 
25828  */
25829 Roo.form.Action.SERVER_INVALID = 'server';
25830  /**
25831  * Connect to Server Failed
25832  * @const 
25833  */
25834 Roo.form.Action.CONNECT_FAILURE = 'connect';
25835 /**
25836  * Reading Data from Server Failed
25837  * @const 
25838  */
25839 Roo.form.Action.LOAD_FAILURE = 'load';
25840
25841 Roo.form.Action.prototype = {
25842     type : 'default',
25843     failureType : undefined,
25844     response : undefined,
25845     result : undefined,
25846
25847     // interface method
25848     run : function(options){
25849
25850     },
25851
25852     // interface method
25853     success : function(response){
25854
25855     },
25856
25857     // interface method
25858     handleResponse : function(response){
25859
25860     },
25861
25862     // default connection failure
25863     failure : function(response){
25864         
25865         this.response = response;
25866         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25867         this.form.afterAction(this, false);
25868     },
25869
25870     processResponse : function(response){
25871         this.response = response;
25872         if(!response.responseText){
25873             return true;
25874         }
25875         this.result = this.handleResponse(response);
25876         return this.result;
25877     },
25878
25879     // utility functions used internally
25880     getUrl : function(appendParams){
25881         var url = this.options.url || this.form.url || this.form.el.dom.action;
25882         if(appendParams){
25883             var p = this.getParams();
25884             if(p){
25885                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25886             }
25887         }
25888         return url;
25889     },
25890
25891     getMethod : function(){
25892         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25893     },
25894
25895     getParams : function(){
25896         var bp = this.form.baseParams;
25897         var p = this.options.params;
25898         if(p){
25899             if(typeof p == "object"){
25900                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25901             }else if(typeof p == 'string' && bp){
25902                 p += '&' + Roo.urlEncode(bp);
25903             }
25904         }else if(bp){
25905             p = Roo.urlEncode(bp);
25906         }
25907         return p;
25908     },
25909
25910     createCallback : function(){
25911         return {
25912             success: this.success,
25913             failure: this.failure,
25914             scope: this,
25915             timeout: (this.form.timeout*1000),
25916             upload: this.form.fileUpload ? this.success : undefined
25917         };
25918     }
25919 };
25920
25921 Roo.form.Action.Submit = function(form, options){
25922     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25923 };
25924
25925 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25926     type : 'submit',
25927
25928     haveProgress : false,
25929     uploadComplete : false,
25930     
25931     // uploadProgress indicator.
25932     uploadProgress : function()
25933     {
25934         if (!this.form.progressUrl) {
25935             return;
25936         }
25937         
25938         if (!this.haveProgress) {
25939             Roo.MessageBox.progress("Uploading", "Uploading");
25940         }
25941         if (this.uploadComplete) {
25942            Roo.MessageBox.hide();
25943            return;
25944         }
25945         
25946         this.haveProgress = true;
25947    
25948         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25949         
25950         var c = new Roo.data.Connection();
25951         c.request({
25952             url : this.form.progressUrl,
25953             params: {
25954                 id : uid
25955             },
25956             method: 'GET',
25957             success : function(req){
25958                //console.log(data);
25959                 var rdata = false;
25960                 var edata;
25961                 try  {
25962                    rdata = Roo.decode(req.responseText)
25963                 } catch (e) {
25964                     Roo.log("Invalid data from server..");
25965                     Roo.log(edata);
25966                     return;
25967                 }
25968                 if (!rdata || !rdata.success) {
25969                     Roo.log(rdata);
25970                     Roo.MessageBox.alert(Roo.encode(rdata));
25971                     return;
25972                 }
25973                 var data = rdata.data;
25974                 
25975                 if (this.uploadComplete) {
25976                    Roo.MessageBox.hide();
25977                    return;
25978                 }
25979                    
25980                 if (data){
25981                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25982                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25983                     );
25984                 }
25985                 this.uploadProgress.defer(2000,this);
25986             },
25987        
25988             failure: function(data) {
25989                 Roo.log('progress url failed ');
25990                 Roo.log(data);
25991             },
25992             scope : this
25993         });
25994            
25995     },
25996     
25997     
25998     run : function()
25999     {
26000         // run get Values on the form, so it syncs any secondary forms.
26001         this.form.getValues();
26002         
26003         var o = this.options;
26004         var method = this.getMethod();
26005         var isPost = method == 'POST';
26006         if(o.clientValidation === false || this.form.isValid()){
26007             
26008             if (this.form.progressUrl) {
26009                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26010                     (new Date() * 1) + '' + Math.random());
26011                     
26012             } 
26013             
26014             
26015             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26016                 form:this.form.el.dom,
26017                 url:this.getUrl(!isPost),
26018                 method: method,
26019                 params:isPost ? this.getParams() : null,
26020                 isUpload: this.form.fileUpload,
26021                 formData : this.form.formData
26022             }));
26023             
26024             this.uploadProgress();
26025
26026         }else if (o.clientValidation !== false){ // client validation failed
26027             this.failureType = Roo.form.Action.CLIENT_INVALID;
26028             this.form.afterAction(this, false);
26029         }
26030     },
26031
26032     success : function(response)
26033     {
26034         this.uploadComplete= true;
26035         if (this.haveProgress) {
26036             Roo.MessageBox.hide();
26037         }
26038         
26039         
26040         var result = this.processResponse(response);
26041         if(result === true || result.success){
26042             this.form.afterAction(this, true);
26043             return;
26044         }
26045         if(result.errors){
26046             this.form.markInvalid(result.errors);
26047             this.failureType = Roo.form.Action.SERVER_INVALID;
26048         }
26049         this.form.afterAction(this, false);
26050     },
26051     failure : function(response)
26052     {
26053         this.uploadComplete= true;
26054         if (this.haveProgress) {
26055             Roo.MessageBox.hide();
26056         }
26057         
26058         this.response = response;
26059         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26060         this.form.afterAction(this, false);
26061     },
26062     
26063     handleResponse : function(response){
26064         if(this.form.errorReader){
26065             var rs = this.form.errorReader.read(response);
26066             var errors = [];
26067             if(rs.records){
26068                 for(var i = 0, len = rs.records.length; i < len; i++) {
26069                     var r = rs.records[i];
26070                     errors[i] = r.data;
26071                 }
26072             }
26073             if(errors.length < 1){
26074                 errors = null;
26075             }
26076             return {
26077                 success : rs.success,
26078                 errors : errors
26079             };
26080         }
26081         var ret = false;
26082         try {
26083             ret = Roo.decode(response.responseText);
26084         } catch (e) {
26085             ret = {
26086                 success: false,
26087                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26088                 errors : []
26089             };
26090         }
26091         return ret;
26092         
26093     }
26094 });
26095
26096
26097 Roo.form.Action.Load = function(form, options){
26098     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26099     this.reader = this.form.reader;
26100 };
26101
26102 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26103     type : 'load',
26104
26105     run : function(){
26106         
26107         Roo.Ajax.request(Roo.apply(
26108                 this.createCallback(), {
26109                     method:this.getMethod(),
26110                     url:this.getUrl(false),
26111                     params:this.getParams()
26112         }));
26113     },
26114
26115     success : function(response){
26116         
26117         var result = this.processResponse(response);
26118         if(result === true || !result.success || !result.data){
26119             this.failureType = Roo.form.Action.LOAD_FAILURE;
26120             this.form.afterAction(this, false);
26121             return;
26122         }
26123         this.form.clearInvalid();
26124         this.form.setValues(result.data);
26125         this.form.afterAction(this, true);
26126     },
26127
26128     handleResponse : function(response){
26129         if(this.form.reader){
26130             var rs = this.form.reader.read(response);
26131             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26132             return {
26133                 success : rs.success,
26134                 data : data
26135             };
26136         }
26137         return Roo.decode(response.responseText);
26138     }
26139 });
26140
26141 Roo.form.Action.ACTION_TYPES = {
26142     'load' : Roo.form.Action.Load,
26143     'submit' : Roo.form.Action.Submit
26144 };/*
26145  * Based on:
26146  * Ext JS Library 1.1.1
26147  * Copyright(c) 2006-2007, Ext JS, LLC.
26148  *
26149  * Originally Released Under LGPL - original licence link has changed is not relivant.
26150  *
26151  * Fork - LGPL
26152  * <script type="text/javascript">
26153  */
26154  
26155 /**
26156  * @class Roo.form.Layout
26157  * @extends Roo.Component
26158  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26159  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26160  * @constructor
26161  * @param {Object} config Configuration options
26162  */
26163 Roo.form.Layout = function(config){
26164     var xitems = [];
26165     if (config.items) {
26166         xitems = config.items;
26167         delete config.items;
26168     }
26169     Roo.form.Layout.superclass.constructor.call(this, config);
26170     this.stack = [];
26171     Roo.each(xitems, this.addxtype, this);
26172      
26173 };
26174
26175 Roo.extend(Roo.form.Layout, Roo.Component, {
26176     /**
26177      * @cfg {String/Object} autoCreate
26178      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26179      */
26180     /**
26181      * @cfg {String/Object/Function} style
26182      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26183      * a function which returns such a specification.
26184      */
26185     /**
26186      * @cfg {String} labelAlign
26187      * Valid values are "left," "top" and "right" (defaults to "left")
26188      */
26189     /**
26190      * @cfg {Number} labelWidth
26191      * Fixed width in pixels of all field labels (defaults to undefined)
26192      */
26193     /**
26194      * @cfg {Boolean} clear
26195      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26196      */
26197     clear : true,
26198     /**
26199      * @cfg {String} labelSeparator
26200      * The separator to use after field labels (defaults to ':')
26201      */
26202     labelSeparator : ':',
26203     /**
26204      * @cfg {Boolean} hideLabels
26205      * True to suppress the display of field labels in this layout (defaults to false)
26206      */
26207     hideLabels : false,
26208
26209     // private
26210     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26211     
26212     isLayout : true,
26213     
26214     // private
26215     onRender : function(ct, position){
26216         if(this.el){ // from markup
26217             this.el = Roo.get(this.el);
26218         }else {  // generate
26219             var cfg = this.getAutoCreate();
26220             this.el = ct.createChild(cfg, position);
26221         }
26222         if(this.style){
26223             this.el.applyStyles(this.style);
26224         }
26225         if(this.labelAlign){
26226             this.el.addClass('x-form-label-'+this.labelAlign);
26227         }
26228         if(this.hideLabels){
26229             this.labelStyle = "display:none";
26230             this.elementStyle = "padding-left:0;";
26231         }else{
26232             if(typeof this.labelWidth == 'number'){
26233                 this.labelStyle = "width:"+this.labelWidth+"px;";
26234                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26235             }
26236             if(this.labelAlign == 'top'){
26237                 this.labelStyle = "width:auto;";
26238                 this.elementStyle = "padding-left:0;";
26239             }
26240         }
26241         var stack = this.stack;
26242         var slen = stack.length;
26243         if(slen > 0){
26244             if(!this.fieldTpl){
26245                 var t = new Roo.Template(
26246                     '<div class="x-form-item {5}">',
26247                         '<label for="{0}" style="{2}">{1}{4}</label>',
26248                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26249                         '</div>',
26250                     '</div><div class="x-form-clear-left"></div>'
26251                 );
26252                 t.disableFormats = true;
26253                 t.compile();
26254                 Roo.form.Layout.prototype.fieldTpl = t;
26255             }
26256             for(var i = 0; i < slen; i++) {
26257                 if(stack[i].isFormField){
26258                     this.renderField(stack[i]);
26259                 }else{
26260                     this.renderComponent(stack[i]);
26261                 }
26262             }
26263         }
26264         if(this.clear){
26265             this.el.createChild({cls:'x-form-clear'});
26266         }
26267     },
26268
26269     // private
26270     renderField : function(f){
26271         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26272                f.id, //0
26273                f.fieldLabel, //1
26274                f.labelStyle||this.labelStyle||'', //2
26275                this.elementStyle||'', //3
26276                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26277                f.itemCls||this.itemCls||''  //5
26278        ], true).getPrevSibling());
26279     },
26280
26281     // private
26282     renderComponent : function(c){
26283         c.render(c.isLayout ? this.el : this.el.createChild());    
26284     },
26285     /**
26286      * Adds a object form elements (using the xtype property as the factory method.)
26287      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26288      * @param {Object} config 
26289      */
26290     addxtype : function(o)
26291     {
26292         // create the lement.
26293         o.form = this.form;
26294         var fe = Roo.factory(o, Roo.form);
26295         this.form.allItems.push(fe);
26296         this.stack.push(fe);
26297         
26298         if (fe.isFormField) {
26299             this.form.items.add(fe);
26300         }
26301          
26302         return fe;
26303     }
26304 });
26305
26306 /**
26307  * @class Roo.form.Column
26308  * @extends Roo.form.Layout
26309  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26310  * @constructor
26311  * @param {Object} config Configuration options
26312  */
26313 Roo.form.Column = function(config){
26314     Roo.form.Column.superclass.constructor.call(this, config);
26315 };
26316
26317 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26318     /**
26319      * @cfg {Number/String} width
26320      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26321      */
26322     /**
26323      * @cfg {String/Object} autoCreate
26324      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26325      */
26326
26327     // private
26328     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26329
26330     // private
26331     onRender : function(ct, position){
26332         Roo.form.Column.superclass.onRender.call(this, ct, position);
26333         if(this.width){
26334             this.el.setWidth(this.width);
26335         }
26336     }
26337 });
26338
26339
26340 /**
26341  * @class Roo.form.Row
26342  * @extends Roo.form.Layout
26343  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26344  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26345  * @constructor
26346  * @param {Object} config Configuration options
26347  */
26348
26349  
26350 Roo.form.Row = function(config){
26351     Roo.form.Row.superclass.constructor.call(this, config);
26352 };
26353  
26354 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26355       /**
26356      * @cfg {Number/String} width
26357      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26358      */
26359     /**
26360      * @cfg {Number/String} height
26361      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26362      */
26363     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26364     
26365     padWidth : 20,
26366     // private
26367     onRender : function(ct, position){
26368         //console.log('row render');
26369         if(!this.rowTpl){
26370             var t = new Roo.Template(
26371                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26372                     '<label for="{0}" style="{2}">{1}{4}</label>',
26373                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26374                     '</div>',
26375                 '</div>'
26376             );
26377             t.disableFormats = true;
26378             t.compile();
26379             Roo.form.Layout.prototype.rowTpl = t;
26380         }
26381         this.fieldTpl = this.rowTpl;
26382         
26383         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26384         var labelWidth = 100;
26385         
26386         if ((this.labelAlign != 'top')) {
26387             if (typeof this.labelWidth == 'number') {
26388                 labelWidth = this.labelWidth
26389             }
26390             this.padWidth =  20 + labelWidth;
26391             
26392         }
26393         
26394         Roo.form.Column.superclass.onRender.call(this, ct, position);
26395         if(this.width){
26396             this.el.setWidth(this.width);
26397         }
26398         if(this.height){
26399             this.el.setHeight(this.height);
26400         }
26401     },
26402     
26403     // private
26404     renderField : function(f){
26405         f.fieldEl = this.fieldTpl.append(this.el, [
26406                f.id, f.fieldLabel,
26407                f.labelStyle||this.labelStyle||'',
26408                this.elementStyle||'',
26409                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26410                f.itemCls||this.itemCls||'',
26411                f.width ? f.width + this.padWidth : 160 + this.padWidth
26412        ],true);
26413     }
26414 });
26415  
26416
26417 /**
26418  * @class Roo.form.FieldSet
26419  * @extends Roo.form.Layout
26420  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26421  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26422  * @constructor
26423  * @param {Object} config Configuration options
26424  */
26425 Roo.form.FieldSet = function(config){
26426     Roo.form.FieldSet.superclass.constructor.call(this, config);
26427 };
26428
26429 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26430     /**
26431      * @cfg {String} legend
26432      * The text to display as the legend for the FieldSet (defaults to '')
26433      */
26434     /**
26435      * @cfg {String/Object} autoCreate
26436      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26437      */
26438
26439     // private
26440     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26441
26442     // private
26443     onRender : function(ct, position){
26444         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26445         if(this.legend){
26446             this.setLegend(this.legend);
26447         }
26448     },
26449
26450     // private
26451     setLegend : function(text){
26452         if(this.rendered){
26453             this.el.child('legend').update(text);
26454         }
26455     }
26456 });/*
26457  * Based on:
26458  * Ext JS Library 1.1.1
26459  * Copyright(c) 2006-2007, Ext JS, LLC.
26460  *
26461  * Originally Released Under LGPL - original licence link has changed is not relivant.
26462  *
26463  * Fork - LGPL
26464  * <script type="text/javascript">
26465  */
26466 /**
26467  * @class Roo.form.VTypes
26468  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26469  * @singleton
26470  */
26471 Roo.form.VTypes = function(){
26472     // closure these in so they are only created once.
26473     var alpha = /^[a-zA-Z_]+$/;
26474     var alphanum = /^[a-zA-Z0-9_]+$/;
26475     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26476     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26477
26478     // All these messages and functions are configurable
26479     return {
26480         /**
26481          * The function used to validate email addresses
26482          * @param {String} value The email address
26483          */
26484         'email' : function(v){
26485             return email.test(v);
26486         },
26487         /**
26488          * The error text to display when the email validation function returns false
26489          * @type String
26490          */
26491         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26492         /**
26493          * The keystroke filter mask to be applied on email input
26494          * @type RegExp
26495          */
26496         'emailMask' : /[a-z0-9_\.\-@]/i,
26497
26498         /**
26499          * The function used to validate URLs
26500          * @param {String} value The URL
26501          */
26502         'url' : function(v){
26503             return url.test(v);
26504         },
26505         /**
26506          * The error text to display when the url validation function returns false
26507          * @type String
26508          */
26509         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26510         
26511         /**
26512          * The function used to validate alpha values
26513          * @param {String} value The value
26514          */
26515         'alpha' : function(v){
26516             return alpha.test(v);
26517         },
26518         /**
26519          * The error text to display when the alpha validation function returns false
26520          * @type String
26521          */
26522         'alphaText' : 'This field should only contain letters and _',
26523         /**
26524          * The keystroke filter mask to be applied on alpha input
26525          * @type RegExp
26526          */
26527         'alphaMask' : /[a-z_]/i,
26528
26529         /**
26530          * The function used to validate alphanumeric values
26531          * @param {String} value The value
26532          */
26533         'alphanum' : function(v){
26534             return alphanum.test(v);
26535         },
26536         /**
26537          * The error text to display when the alphanumeric validation function returns false
26538          * @type String
26539          */
26540         'alphanumText' : 'This field should only contain letters, numbers and _',
26541         /**
26542          * The keystroke filter mask to be applied on alphanumeric input
26543          * @type RegExp
26544          */
26545         'alphanumMask' : /[a-z0-9_]/i
26546     };
26547 }();//<script type="text/javascript">
26548
26549 /**
26550  * @class Roo.form.FCKeditor
26551  * @extends Roo.form.TextArea
26552  * Wrapper around the FCKEditor http://www.fckeditor.net
26553  * @constructor
26554  * Creates a new FCKeditor
26555  * @param {Object} config Configuration options
26556  */
26557 Roo.form.FCKeditor = function(config){
26558     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26559     this.addEvents({
26560          /**
26561          * @event editorinit
26562          * Fired when the editor is initialized - you can add extra handlers here..
26563          * @param {FCKeditor} this
26564          * @param {Object} the FCK object.
26565          */
26566         editorinit : true
26567     });
26568     
26569     
26570 };
26571 Roo.form.FCKeditor.editors = { };
26572 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26573 {
26574     //defaultAutoCreate : {
26575     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26576     //},
26577     // private
26578     /**
26579      * @cfg {Object} fck options - see fck manual for details.
26580      */
26581     fckconfig : false,
26582     
26583     /**
26584      * @cfg {Object} fck toolbar set (Basic or Default)
26585      */
26586     toolbarSet : 'Basic',
26587     /**
26588      * @cfg {Object} fck BasePath
26589      */ 
26590     basePath : '/fckeditor/',
26591     
26592     
26593     frame : false,
26594     
26595     value : '',
26596     
26597    
26598     onRender : function(ct, position)
26599     {
26600         if(!this.el){
26601             this.defaultAutoCreate = {
26602                 tag: "textarea",
26603                 style:"width:300px;height:60px;",
26604                 autocomplete: "new-password"
26605             };
26606         }
26607         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26608         /*
26609         if(this.grow){
26610             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26611             if(this.preventScrollbars){
26612                 this.el.setStyle("overflow", "hidden");
26613             }
26614             this.el.setHeight(this.growMin);
26615         }
26616         */
26617         //console.log('onrender' + this.getId() );
26618         Roo.form.FCKeditor.editors[this.getId()] = this;
26619          
26620
26621         this.replaceTextarea() ;
26622         
26623     },
26624     
26625     getEditor : function() {
26626         return this.fckEditor;
26627     },
26628     /**
26629      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26630      * @param {Mixed} value The value to set
26631      */
26632     
26633     
26634     setValue : function(value)
26635     {
26636         //console.log('setValue: ' + value);
26637         
26638         if(typeof(value) == 'undefined') { // not sure why this is happending...
26639             return;
26640         }
26641         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26642         
26643         //if(!this.el || !this.getEditor()) {
26644         //    this.value = value;
26645             //this.setValue.defer(100,this,[value]);    
26646         //    return;
26647         //} 
26648         
26649         if(!this.getEditor()) {
26650             return;
26651         }
26652         
26653         this.getEditor().SetData(value);
26654         
26655         //
26656
26657     },
26658
26659     /**
26660      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26661      * @return {Mixed} value The field value
26662      */
26663     getValue : function()
26664     {
26665         
26666         if (this.frame && this.frame.dom.style.display == 'none') {
26667             return Roo.form.FCKeditor.superclass.getValue.call(this);
26668         }
26669         
26670         if(!this.el || !this.getEditor()) {
26671            
26672            // this.getValue.defer(100,this); 
26673             return this.value;
26674         }
26675        
26676         
26677         var value=this.getEditor().GetData();
26678         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26679         return Roo.form.FCKeditor.superclass.getValue.call(this);
26680         
26681
26682     },
26683
26684     /**
26685      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26686      * @return {Mixed} value The field value
26687      */
26688     getRawValue : function()
26689     {
26690         if (this.frame && this.frame.dom.style.display == 'none') {
26691             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26692         }
26693         
26694         if(!this.el || !this.getEditor()) {
26695             //this.getRawValue.defer(100,this); 
26696             return this.value;
26697             return;
26698         }
26699         
26700         
26701         
26702         var value=this.getEditor().GetData();
26703         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26704         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26705          
26706     },
26707     
26708     setSize : function(w,h) {
26709         
26710         
26711         
26712         //if (this.frame && this.frame.dom.style.display == 'none') {
26713         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26714         //    return;
26715         //}
26716         //if(!this.el || !this.getEditor()) {
26717         //    this.setSize.defer(100,this, [w,h]); 
26718         //    return;
26719         //}
26720         
26721         
26722         
26723         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26724         
26725         this.frame.dom.setAttribute('width', w);
26726         this.frame.dom.setAttribute('height', h);
26727         this.frame.setSize(w,h);
26728         
26729     },
26730     
26731     toggleSourceEdit : function(value) {
26732         
26733       
26734          
26735         this.el.dom.style.display = value ? '' : 'none';
26736         this.frame.dom.style.display = value ?  'none' : '';
26737         
26738     },
26739     
26740     
26741     focus: function(tag)
26742     {
26743         if (this.frame.dom.style.display == 'none') {
26744             return Roo.form.FCKeditor.superclass.focus.call(this);
26745         }
26746         if(!this.el || !this.getEditor()) {
26747             this.focus.defer(100,this, [tag]); 
26748             return;
26749         }
26750         
26751         
26752         
26753         
26754         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26755         this.getEditor().Focus();
26756         if (tgs.length) {
26757             if (!this.getEditor().Selection.GetSelection()) {
26758                 this.focus.defer(100,this, [tag]); 
26759                 return;
26760             }
26761             
26762             
26763             var r = this.getEditor().EditorDocument.createRange();
26764             r.setStart(tgs[0],0);
26765             r.setEnd(tgs[0],0);
26766             this.getEditor().Selection.GetSelection().removeAllRanges();
26767             this.getEditor().Selection.GetSelection().addRange(r);
26768             this.getEditor().Focus();
26769         }
26770         
26771     },
26772     
26773     
26774     
26775     replaceTextarea : function()
26776     {
26777         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26778             return ;
26779         }
26780         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26781         //{
26782             // We must check the elements firstly using the Id and then the name.
26783         var oTextarea = document.getElementById( this.getId() );
26784         
26785         var colElementsByName = document.getElementsByName( this.getId() ) ;
26786          
26787         oTextarea.style.display = 'none' ;
26788
26789         if ( oTextarea.tabIndex ) {            
26790             this.TabIndex = oTextarea.tabIndex ;
26791         }
26792         
26793         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26794         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26795         this.frame = Roo.get(this.getId() + '___Frame')
26796     },
26797     
26798     _getConfigHtml : function()
26799     {
26800         var sConfig = '' ;
26801
26802         for ( var o in this.fckconfig ) {
26803             sConfig += sConfig.length > 0  ? '&amp;' : '';
26804             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26805         }
26806
26807         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26808     },
26809     
26810     
26811     _getIFrameHtml : function()
26812     {
26813         var sFile = 'fckeditor.html' ;
26814         /* no idea what this is about..
26815         try
26816         {
26817             if ( (/fcksource=true/i).test( window.top.location.search ) )
26818                 sFile = 'fckeditor.original.html' ;
26819         }
26820         catch (e) { 
26821         */
26822
26823         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26824         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26825         
26826         
26827         var html = '<iframe id="' + this.getId() +
26828             '___Frame" src="' + sLink +
26829             '" width="' + this.width +
26830             '" height="' + this.height + '"' +
26831             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26832             ' frameborder="0" scrolling="no"></iframe>' ;
26833
26834         return html ;
26835     },
26836     
26837     _insertHtmlBefore : function( html, element )
26838     {
26839         if ( element.insertAdjacentHTML )       {
26840             // IE
26841             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26842         } else { // Gecko
26843             var oRange = document.createRange() ;
26844             oRange.setStartBefore( element ) ;
26845             var oFragment = oRange.createContextualFragment( html );
26846             element.parentNode.insertBefore( oFragment, element ) ;
26847         }
26848     }
26849     
26850     
26851   
26852     
26853     
26854     
26855     
26856
26857 });
26858
26859 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26860
26861 function FCKeditor_OnComplete(editorInstance){
26862     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26863     f.fckEditor = editorInstance;
26864     //console.log("loaded");
26865     f.fireEvent('editorinit', f, editorInstance);
26866
26867   
26868
26869  
26870
26871
26872
26873
26874
26875
26876
26877
26878
26879
26880
26881
26882
26883
26884
26885 //<script type="text/javascript">
26886 /**
26887  * @class Roo.form.GridField
26888  * @extends Roo.form.Field
26889  * Embed a grid (or editable grid into a form)
26890  * STATUS ALPHA
26891  * 
26892  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26893  * it needs 
26894  * xgrid.store = Roo.data.Store
26895  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26896  * xgrid.store.reader = Roo.data.JsonReader 
26897  * 
26898  * 
26899  * @constructor
26900  * Creates a new GridField
26901  * @param {Object} config Configuration options
26902  */
26903 Roo.form.GridField = function(config){
26904     Roo.form.GridField.superclass.constructor.call(this, config);
26905      
26906 };
26907
26908 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26909     /**
26910      * @cfg {Number} width  - used to restrict width of grid..
26911      */
26912     width : 100,
26913     /**
26914      * @cfg {Number} height - used to restrict height of grid..
26915      */
26916     height : 50,
26917      /**
26918      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26919          * 
26920          *}
26921      */
26922     xgrid : false, 
26923     /**
26924      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26925      * {tag: "input", type: "checkbox", autocomplete: "off"})
26926      */
26927    // defaultAutoCreate : { tag: 'div' },
26928     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26929     /**
26930      * @cfg {String} addTitle Text to include for adding a title.
26931      */
26932     addTitle : false,
26933     //
26934     onResize : function(){
26935         Roo.form.Field.superclass.onResize.apply(this, arguments);
26936     },
26937
26938     initEvents : function(){
26939         // Roo.form.Checkbox.superclass.initEvents.call(this);
26940         // has no events...
26941        
26942     },
26943
26944
26945     getResizeEl : function(){
26946         return this.wrap;
26947     },
26948
26949     getPositionEl : function(){
26950         return this.wrap;
26951     },
26952
26953     // private
26954     onRender : function(ct, position){
26955         
26956         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26957         var style = this.style;
26958         delete this.style;
26959         
26960         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26961         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26962         this.viewEl = this.wrap.createChild({ tag: 'div' });
26963         if (style) {
26964             this.viewEl.applyStyles(style);
26965         }
26966         if (this.width) {
26967             this.viewEl.setWidth(this.width);
26968         }
26969         if (this.height) {
26970             this.viewEl.setHeight(this.height);
26971         }
26972         //if(this.inputValue !== undefined){
26973         //this.setValue(this.value);
26974         
26975         
26976         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26977         
26978         
26979         this.grid.render();
26980         this.grid.getDataSource().on('remove', this.refreshValue, this);
26981         this.grid.getDataSource().on('update', this.refreshValue, this);
26982         this.grid.on('afteredit', this.refreshValue, this);
26983  
26984     },
26985      
26986     
26987     /**
26988      * Sets the value of the item. 
26989      * @param {String} either an object  or a string..
26990      */
26991     setValue : function(v){
26992         //this.value = v;
26993         v = v || []; // empty set..
26994         // this does not seem smart - it really only affects memoryproxy grids..
26995         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26996             var ds = this.grid.getDataSource();
26997             // assumes a json reader..
26998             var data = {}
26999             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27000             ds.loadData( data);
27001         }
27002         // clear selection so it does not get stale.
27003         if (this.grid.sm) { 
27004             this.grid.sm.clearSelections();
27005         }
27006         
27007         Roo.form.GridField.superclass.setValue.call(this, v);
27008         this.refreshValue();
27009         // should load data in the grid really....
27010     },
27011     
27012     // private
27013     refreshValue: function() {
27014          var val = [];
27015         this.grid.getDataSource().each(function(r) {
27016             val.push(r.data);
27017         });
27018         this.el.dom.value = Roo.encode(val);
27019     }
27020     
27021      
27022     
27023     
27024 });/*
27025  * Based on:
27026  * Ext JS Library 1.1.1
27027  * Copyright(c) 2006-2007, Ext JS, LLC.
27028  *
27029  * Originally Released Under LGPL - original licence link has changed is not relivant.
27030  *
27031  * Fork - LGPL
27032  * <script type="text/javascript">
27033  */
27034 /**
27035  * @class Roo.form.DisplayField
27036  * @extends Roo.form.Field
27037  * A generic Field to display non-editable data.
27038  * @cfg {Boolean} closable (true|false) default false
27039  * @constructor
27040  * Creates a new Display Field item.
27041  * @param {Object} config Configuration options
27042  */
27043 Roo.form.DisplayField = function(config){
27044     Roo.form.DisplayField.superclass.constructor.call(this, config);
27045     
27046     this.addEvents({
27047         /**
27048          * @event close
27049          * Fires after the click the close btn
27050              * @param {Roo.form.DisplayField} this
27051              */
27052         close : true
27053     });
27054 };
27055
27056 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27057     inputType:      'hidden',
27058     allowBlank:     true,
27059     readOnly:         true,
27060     
27061  
27062     /**
27063      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27064      */
27065     focusClass : undefined,
27066     /**
27067      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27068      */
27069     fieldClass: 'x-form-field',
27070     
27071      /**
27072      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27073      */
27074     valueRenderer: undefined,
27075     
27076     width: 100,
27077     /**
27078      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27079      * {tag: "input", type: "checkbox", autocomplete: "off"})
27080      */
27081      
27082  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27083  
27084     closable : false,
27085     
27086     onResize : function(){
27087         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27088         
27089     },
27090
27091     initEvents : function(){
27092         // Roo.form.Checkbox.superclass.initEvents.call(this);
27093         // has no events...
27094         
27095         if(this.closable){
27096             this.closeEl.on('click', this.onClose, this);
27097         }
27098        
27099     },
27100
27101
27102     getResizeEl : function(){
27103         return this.wrap;
27104     },
27105
27106     getPositionEl : function(){
27107         return this.wrap;
27108     },
27109
27110     // private
27111     onRender : function(ct, position){
27112         
27113         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27114         //if(this.inputValue !== undefined){
27115         this.wrap = this.el.wrap();
27116         
27117         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27118         
27119         if(this.closable){
27120             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27121         }
27122         
27123         if (this.bodyStyle) {
27124             this.viewEl.applyStyles(this.bodyStyle);
27125         }
27126         //this.viewEl.setStyle('padding', '2px');
27127         
27128         this.setValue(this.value);
27129         
27130     },
27131 /*
27132     // private
27133     initValue : Roo.emptyFn,
27134
27135   */
27136
27137         // private
27138     onClick : function(){
27139         
27140     },
27141
27142     /**
27143      * Sets the checked state of the checkbox.
27144      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27145      */
27146     setValue : function(v){
27147         this.value = v;
27148         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27149         // this might be called before we have a dom element..
27150         if (!this.viewEl) {
27151             return;
27152         }
27153         this.viewEl.dom.innerHTML = html;
27154         Roo.form.DisplayField.superclass.setValue.call(this, v);
27155
27156     },
27157     
27158     onClose : function(e)
27159     {
27160         e.preventDefault();
27161         
27162         this.fireEvent('close', this);
27163     }
27164 });/*
27165  * 
27166  * Licence- LGPL
27167  * 
27168  */
27169
27170 /**
27171  * @class Roo.form.DayPicker
27172  * @extends Roo.form.Field
27173  * A Day picker show [M] [T] [W] ....
27174  * @constructor
27175  * Creates a new Day Picker
27176  * @param {Object} config Configuration options
27177  */
27178 Roo.form.DayPicker= function(config){
27179     Roo.form.DayPicker.superclass.constructor.call(this, config);
27180      
27181 };
27182
27183 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27184     /**
27185      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27186      */
27187     focusClass : undefined,
27188     /**
27189      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27190      */
27191     fieldClass: "x-form-field",
27192    
27193     /**
27194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27195      * {tag: "input", type: "checkbox", autocomplete: "off"})
27196      */
27197     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27198     
27199    
27200     actionMode : 'viewEl', 
27201     //
27202     // private
27203  
27204     inputType : 'hidden',
27205     
27206      
27207     inputElement: false, // real input element?
27208     basedOn: false, // ????
27209     
27210     isFormField: true, // not sure where this is needed!!!!
27211
27212     onResize : function(){
27213         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27214         if(!this.boxLabel){
27215             this.el.alignTo(this.wrap, 'c-c');
27216         }
27217     },
27218
27219     initEvents : function(){
27220         Roo.form.Checkbox.superclass.initEvents.call(this);
27221         this.el.on("click", this.onClick,  this);
27222         this.el.on("change", this.onClick,  this);
27223     },
27224
27225
27226     getResizeEl : function(){
27227         return this.wrap;
27228     },
27229
27230     getPositionEl : function(){
27231         return this.wrap;
27232     },
27233
27234     
27235     // private
27236     onRender : function(ct, position){
27237         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27238        
27239         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27240         
27241         var r1 = '<table><tr>';
27242         var r2 = '<tr class="x-form-daypick-icons">';
27243         for (var i=0; i < 7; i++) {
27244             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27245             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27246         }
27247         
27248         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27249         viewEl.select('img').on('click', this.onClick, this);
27250         this.viewEl = viewEl;   
27251         
27252         
27253         // this will not work on Chrome!!!
27254         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27255         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27256         
27257         
27258           
27259
27260     },
27261
27262     // private
27263     initValue : Roo.emptyFn,
27264
27265     /**
27266      * Returns the checked state of the checkbox.
27267      * @return {Boolean} True if checked, else false
27268      */
27269     getValue : function(){
27270         return this.el.dom.value;
27271         
27272     },
27273
27274         // private
27275     onClick : function(e){ 
27276         //this.setChecked(!this.checked);
27277         Roo.get(e.target).toggleClass('x-menu-item-checked');
27278         this.refreshValue();
27279         //if(this.el.dom.checked != this.checked){
27280         //    this.setValue(this.el.dom.checked);
27281        // }
27282     },
27283     
27284     // private
27285     refreshValue : function()
27286     {
27287         var val = '';
27288         this.viewEl.select('img',true).each(function(e,i,n)  {
27289             val += e.is(".x-menu-item-checked") ? String(n) : '';
27290         });
27291         this.setValue(val, true);
27292     },
27293
27294     /**
27295      * Sets the checked state of the checkbox.
27296      * On is always based on a string comparison between inputValue and the param.
27297      * @param {Boolean/String} value - the value to set 
27298      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27299      */
27300     setValue : function(v,suppressEvent){
27301         if (!this.el.dom) {
27302             return;
27303         }
27304         var old = this.el.dom.value ;
27305         this.el.dom.value = v;
27306         if (suppressEvent) {
27307             return ;
27308         }
27309          
27310         // update display..
27311         this.viewEl.select('img',true).each(function(e,i,n)  {
27312             
27313             var on = e.is(".x-menu-item-checked");
27314             var newv = v.indexOf(String(n)) > -1;
27315             if (on != newv) {
27316                 e.toggleClass('x-menu-item-checked');
27317             }
27318             
27319         });
27320         
27321         
27322         this.fireEvent('change', this, v, old);
27323         
27324         
27325     },
27326    
27327     // handle setting of hidden value by some other method!!?!?
27328     setFromHidden: function()
27329     {
27330         if(!this.el){
27331             return;
27332         }
27333         //console.log("SET FROM HIDDEN");
27334         //alert('setFrom hidden');
27335         this.setValue(this.el.dom.value);
27336     },
27337     
27338     onDestroy : function()
27339     {
27340         if(this.viewEl){
27341             Roo.get(this.viewEl).remove();
27342         }
27343          
27344         Roo.form.DayPicker.superclass.onDestroy.call(this);
27345     }
27346
27347 });/*
27348  * RooJS Library 1.1.1
27349  * Copyright(c) 2008-2011  Alan Knowles
27350  *
27351  * License - LGPL
27352  */
27353  
27354
27355 /**
27356  * @class Roo.form.ComboCheck
27357  * @extends Roo.form.ComboBox
27358  * A combobox for multiple select items.
27359  *
27360  * FIXME - could do with a reset button..
27361  * 
27362  * @constructor
27363  * Create a new ComboCheck
27364  * @param {Object} config Configuration options
27365  */
27366 Roo.form.ComboCheck = function(config){
27367     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27368     // should verify some data...
27369     // like
27370     // hiddenName = required..
27371     // displayField = required
27372     // valudField == required
27373     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27374     var _t = this;
27375     Roo.each(req, function(e) {
27376         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27377             throw "Roo.form.ComboCheck : missing value for: " + e;
27378         }
27379     });
27380     
27381     
27382 };
27383
27384 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27385      
27386      
27387     editable : false,
27388      
27389     selectedClass: 'x-menu-item-checked', 
27390     
27391     // private
27392     onRender : function(ct, position){
27393         var _t = this;
27394         
27395         
27396         
27397         if(!this.tpl){
27398             var cls = 'x-combo-list';
27399
27400             
27401             this.tpl =  new Roo.Template({
27402                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27403                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27404                    '<span>{' + this.displayField + '}</span>' +
27405                     '</div>' 
27406                 
27407             });
27408         }
27409  
27410         
27411         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27412         this.view.singleSelect = false;
27413         this.view.multiSelect = true;
27414         this.view.toggleSelect = true;
27415         this.pageTb.add(new Roo.Toolbar.Fill(), {
27416             
27417             text: 'Done',
27418             handler: function()
27419             {
27420                 _t.collapse();
27421             }
27422         });
27423     },
27424     
27425     onViewOver : function(e, t){
27426         // do nothing...
27427         return;
27428         
27429     },
27430     
27431     onViewClick : function(doFocus,index){
27432         return;
27433         
27434     },
27435     select: function () {
27436         //Roo.log("SELECT CALLED");
27437     },
27438      
27439     selectByValue : function(xv, scrollIntoView){
27440         var ar = this.getValueArray();
27441         var sels = [];
27442         
27443         Roo.each(ar, function(v) {
27444             if(v === undefined || v === null){
27445                 return;
27446             }
27447             var r = this.findRecord(this.valueField, v);
27448             if(r){
27449                 sels.push(this.store.indexOf(r))
27450                 
27451             }
27452         },this);
27453         this.view.select(sels);
27454         return false;
27455     },
27456     
27457     
27458     
27459     onSelect : function(record, index){
27460        // Roo.log("onselect Called");
27461        // this is only called by the clear button now..
27462         this.view.clearSelections();
27463         this.setValue('[]');
27464         if (this.value != this.valueBefore) {
27465             this.fireEvent('change', this, this.value, this.valueBefore);
27466             this.valueBefore = this.value;
27467         }
27468     },
27469     getValueArray : function()
27470     {
27471         var ar = [] ;
27472         
27473         try {
27474             //Roo.log(this.value);
27475             if (typeof(this.value) == 'undefined') {
27476                 return [];
27477             }
27478             var ar = Roo.decode(this.value);
27479             return  ar instanceof Array ? ar : []; //?? valid?
27480             
27481         } catch(e) {
27482             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27483             return [];
27484         }
27485          
27486     },
27487     expand : function ()
27488     {
27489         
27490         Roo.form.ComboCheck.superclass.expand.call(this);
27491         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27492         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27493         
27494
27495     },
27496     
27497     collapse : function(){
27498         Roo.form.ComboCheck.superclass.collapse.call(this);
27499         var sl = this.view.getSelectedIndexes();
27500         var st = this.store;
27501         var nv = [];
27502         var tv = [];
27503         var r;
27504         Roo.each(sl, function(i) {
27505             r = st.getAt(i);
27506             nv.push(r.get(this.valueField));
27507         },this);
27508         this.setValue(Roo.encode(nv));
27509         if (this.value != this.valueBefore) {
27510
27511             this.fireEvent('change', this, this.value, this.valueBefore);
27512             this.valueBefore = this.value;
27513         }
27514         
27515     },
27516     
27517     setValue : function(v){
27518         // Roo.log(v);
27519         this.value = v;
27520         
27521         var vals = this.getValueArray();
27522         var tv = [];
27523         Roo.each(vals, function(k) {
27524             var r = this.findRecord(this.valueField, k);
27525             if(r){
27526                 tv.push(r.data[this.displayField]);
27527             }else if(this.valueNotFoundText !== undefined){
27528                 tv.push( this.valueNotFoundText );
27529             }
27530         },this);
27531        // Roo.log(tv);
27532         
27533         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27534         this.hiddenField.value = v;
27535         this.value = v;
27536     }
27537     
27538 });/*
27539  * Based on:
27540  * Ext JS Library 1.1.1
27541  * Copyright(c) 2006-2007, Ext JS, LLC.
27542  *
27543  * Originally Released Under LGPL - original licence link has changed is not relivant.
27544  *
27545  * Fork - LGPL
27546  * <script type="text/javascript">
27547  */
27548  
27549 /**
27550  * @class Roo.form.Signature
27551  * @extends Roo.form.Field
27552  * Signature field.  
27553  * @constructor
27554  * 
27555  * @param {Object} config Configuration options
27556  */
27557
27558 Roo.form.Signature = function(config){
27559     Roo.form.Signature.superclass.constructor.call(this, config);
27560     
27561     this.addEvents({// not in used??
27562          /**
27563          * @event confirm
27564          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27565              * @param {Roo.form.Signature} combo This combo box
27566              */
27567         'confirm' : true,
27568         /**
27569          * @event reset
27570          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27571              * @param {Roo.form.ComboBox} combo This combo box
27572              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27573              */
27574         'reset' : true
27575     });
27576 };
27577
27578 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27579     /**
27580      * @cfg {Object} labels Label to use when rendering a form.
27581      * defaults to 
27582      * labels : { 
27583      *      clear : "Clear",
27584      *      confirm : "Confirm"
27585      *  }
27586      */
27587     labels : { 
27588         clear : "Clear",
27589         confirm : "Confirm"
27590     },
27591     /**
27592      * @cfg {Number} width The signature panel width (defaults to 300)
27593      */
27594     width: 300,
27595     /**
27596      * @cfg {Number} height The signature panel height (defaults to 100)
27597      */
27598     height : 100,
27599     /**
27600      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27601      */
27602     allowBlank : false,
27603     
27604     //private
27605     // {Object} signPanel The signature SVG panel element (defaults to {})
27606     signPanel : {},
27607     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27608     isMouseDown : false,
27609     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27610     isConfirmed : false,
27611     // {String} signatureTmp SVG mapping string (defaults to empty string)
27612     signatureTmp : '',
27613     
27614     
27615     defaultAutoCreate : { // modified by initCompnoent..
27616         tag: "input",
27617         type:"hidden"
27618     },
27619
27620     // private
27621     onRender : function(ct, position){
27622         
27623         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27624         
27625         this.wrap = this.el.wrap({
27626             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27627         });
27628         
27629         this.createToolbar(this);
27630         this.signPanel = this.wrap.createChild({
27631                 tag: 'div',
27632                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27633             }, this.el
27634         );
27635             
27636         this.svgID = Roo.id();
27637         this.svgEl = this.signPanel.createChild({
27638               xmlns : 'http://www.w3.org/2000/svg',
27639               tag : 'svg',
27640               id : this.svgID + "-svg",
27641               width: this.width,
27642               height: this.height,
27643               viewBox: '0 0 '+this.width+' '+this.height,
27644               cn : [
27645                 {
27646                     tag: "rect",
27647                     id: this.svgID + "-svg-r",
27648                     width: this.width,
27649                     height: this.height,
27650                     fill: "#ffa"
27651                 },
27652                 {
27653                     tag: "line",
27654                     id: this.svgID + "-svg-l",
27655                     x1: "0", // start
27656                     y1: (this.height*0.8), // start set the line in 80% of height
27657                     x2: this.width, // end
27658                     y2: (this.height*0.8), // end set the line in 80% of height
27659                     'stroke': "#666",
27660                     'stroke-width': "1",
27661                     'stroke-dasharray': "3",
27662                     'shape-rendering': "crispEdges",
27663                     'pointer-events': "none"
27664                 },
27665                 {
27666                     tag: "path",
27667                     id: this.svgID + "-svg-p",
27668                     'stroke': "navy",
27669                     'stroke-width': "3",
27670                     'fill': "none",
27671                     'pointer-events': 'none'
27672                 }
27673               ]
27674         });
27675         this.createSVG();
27676         this.svgBox = this.svgEl.dom.getScreenCTM();
27677     },
27678     createSVG : function(){ 
27679         var svg = this.signPanel;
27680         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27681         var t = this;
27682
27683         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27684         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27685         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27686         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27687         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27688         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27689         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27690         
27691     },
27692     isTouchEvent : function(e){
27693         return e.type.match(/^touch/);
27694     },
27695     getCoords : function (e) {
27696         var pt    = this.svgEl.dom.createSVGPoint();
27697         pt.x = e.clientX; 
27698         pt.y = e.clientY;
27699         if (this.isTouchEvent(e)) {
27700             pt.x =  e.targetTouches[0].clientX;
27701             pt.y = e.targetTouches[0].clientY;
27702         }
27703         var a = this.svgEl.dom.getScreenCTM();
27704         var b = a.inverse();
27705         var mx = pt.matrixTransform(b);
27706         return mx.x + ',' + mx.y;
27707     },
27708     //mouse event headler 
27709     down : function (e) {
27710         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27711         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27712         
27713         this.isMouseDown = true;
27714         
27715         e.preventDefault();
27716     },
27717     move : function (e) {
27718         if (this.isMouseDown) {
27719             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27720             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27721         }
27722         
27723         e.preventDefault();
27724     },
27725     up : function (e) {
27726         this.isMouseDown = false;
27727         var sp = this.signatureTmp.split(' ');
27728         
27729         if(sp.length > 1){
27730             if(!sp[sp.length-2].match(/^L/)){
27731                 sp.pop();
27732                 sp.pop();
27733                 sp.push("");
27734                 this.signatureTmp = sp.join(" ");
27735             }
27736         }
27737         if(this.getValue() != this.signatureTmp){
27738             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27739             this.isConfirmed = false;
27740         }
27741         e.preventDefault();
27742     },
27743     
27744     /**
27745      * Protected method that will not generally be called directly. It
27746      * is called when the editor creates its toolbar. Override this method if you need to
27747      * add custom toolbar buttons.
27748      * @param {HtmlEditor} editor
27749      */
27750     createToolbar : function(editor){
27751          function btn(id, toggle, handler){
27752             var xid = fid + '-'+ id ;
27753             return {
27754                 id : xid,
27755                 cmd : id,
27756                 cls : 'x-btn-icon x-edit-'+id,
27757                 enableToggle:toggle !== false,
27758                 scope: editor, // was editor...
27759                 handler:handler||editor.relayBtnCmd,
27760                 clickEvent:'mousedown',
27761                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27762                 tabIndex:-1
27763             };
27764         }
27765         
27766         
27767         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27768         this.tb = tb;
27769         this.tb.add(
27770            {
27771                 cls : ' x-signature-btn x-signature-'+id,
27772                 scope: editor, // was editor...
27773                 handler: this.reset,
27774                 clickEvent:'mousedown',
27775                 text: this.labels.clear
27776             },
27777             {
27778                  xtype : 'Fill',
27779                  xns: Roo.Toolbar
27780             }, 
27781             {
27782                 cls : '  x-signature-btn x-signature-'+id,
27783                 scope: editor, // was editor...
27784                 handler: this.confirmHandler,
27785                 clickEvent:'mousedown',
27786                 text: this.labels.confirm
27787             }
27788         );
27789     
27790     },
27791     //public
27792     /**
27793      * when user is clicked confirm then show this image.....
27794      * 
27795      * @return {String} Image Data URI
27796      */
27797     getImageDataURI : function(){
27798         var svg = this.svgEl.dom.parentNode.innerHTML;
27799         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27800         return src; 
27801     },
27802     /**
27803      * 
27804      * @return {Boolean} this.isConfirmed
27805      */
27806     getConfirmed : function(){
27807         return this.isConfirmed;
27808     },
27809     /**
27810      * 
27811      * @return {Number} this.width
27812      */
27813     getWidth : function(){
27814         return this.width;
27815     },
27816     /**
27817      * 
27818      * @return {Number} this.height
27819      */
27820     getHeight : function(){
27821         return this.height;
27822     },
27823     // private
27824     getSignature : function(){
27825         return this.signatureTmp;
27826     },
27827     // private
27828     reset : function(){
27829         this.signatureTmp = '';
27830         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27831         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27832         this.isConfirmed = false;
27833         Roo.form.Signature.superclass.reset.call(this);
27834     },
27835     setSignature : function(s){
27836         this.signatureTmp = s;
27837         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27838         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27839         this.setValue(s);
27840         this.isConfirmed = false;
27841         Roo.form.Signature.superclass.reset.call(this);
27842     }, 
27843     test : function(){
27844 //        Roo.log(this.signPanel.dom.contentWindow.up())
27845     },
27846     //private
27847     setConfirmed : function(){
27848         
27849         
27850         
27851 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27852     },
27853     // private
27854     confirmHandler : function(){
27855         if(!this.getSignature()){
27856             return;
27857         }
27858         
27859         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27860         this.setValue(this.getSignature());
27861         this.isConfirmed = true;
27862         
27863         this.fireEvent('confirm', this);
27864     },
27865     // private
27866     // Subclasses should provide the validation implementation by overriding this
27867     validateValue : function(value){
27868         if(this.allowBlank){
27869             return true;
27870         }
27871         
27872         if(this.isConfirmed){
27873             return true;
27874         }
27875         return false;
27876     }
27877 });/*
27878  * Based on:
27879  * Ext JS Library 1.1.1
27880  * Copyright(c) 2006-2007, Ext JS, LLC.
27881  *
27882  * Originally Released Under LGPL - original licence link has changed is not relivant.
27883  *
27884  * Fork - LGPL
27885  * <script type="text/javascript">
27886  */
27887  
27888
27889 /**
27890  * @class Roo.form.ComboBox
27891  * @extends Roo.form.TriggerField
27892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27893  * @constructor
27894  * Create a new ComboBox.
27895  * @param {Object} config Configuration options
27896  */
27897 Roo.form.Select = function(config){
27898     Roo.form.Select.superclass.constructor.call(this, config);
27899      
27900 };
27901
27902 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27903     /**
27904      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27905      */
27906     /**
27907      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27908      * rendering into an Roo.Editor, defaults to false)
27909      */
27910     /**
27911      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27912      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27913      */
27914     /**
27915      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27916      */
27917     /**
27918      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27919      * the dropdown list (defaults to undefined, with no header element)
27920      */
27921
27922      /**
27923      * @cfg {String/Roo.Template} tpl The template to use to render the output
27924      */
27925      
27926     // private
27927     defaultAutoCreate : {tag: "select"  },
27928     /**
27929      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27930      */
27931     listWidth: undefined,
27932     /**
27933      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27934      * mode = 'remote' or 'text' if mode = 'local')
27935      */
27936     displayField: undefined,
27937     /**
27938      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27939      * mode = 'remote' or 'value' if mode = 'local'). 
27940      * Note: use of a valueField requires the user make a selection
27941      * in order for a value to be mapped.
27942      */
27943     valueField: undefined,
27944     
27945     
27946     /**
27947      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27948      * field's data value (defaults to the underlying DOM element's name)
27949      */
27950     hiddenName: undefined,
27951     /**
27952      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27953      */
27954     listClass: '',
27955     /**
27956      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27957      */
27958     selectedClass: 'x-combo-selected',
27959     /**
27960      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27961      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27962      * which displays a downward arrow icon).
27963      */
27964     triggerClass : 'x-form-arrow-trigger',
27965     /**
27966      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27967      */
27968     shadow:'sides',
27969     /**
27970      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27971      * anchor positions (defaults to 'tl-bl')
27972      */
27973     listAlign: 'tl-bl?',
27974     /**
27975      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27976      */
27977     maxHeight: 300,
27978     /**
27979      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27980      * query specified by the allQuery config option (defaults to 'query')
27981      */
27982     triggerAction: 'query',
27983     /**
27984      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27985      * (defaults to 4, does not apply if editable = false)
27986      */
27987     minChars : 4,
27988     /**
27989      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27990      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27991      */
27992     typeAhead: false,
27993     /**
27994      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27995      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27996      */
27997     queryDelay: 500,
27998     /**
27999      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28000      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28001      */
28002     pageSize: 0,
28003     /**
28004      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28005      * when editable = true (defaults to false)
28006      */
28007     selectOnFocus:false,
28008     /**
28009      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28010      */
28011     queryParam: 'query',
28012     /**
28013      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28014      * when mode = 'remote' (defaults to 'Loading...')
28015      */
28016     loadingText: 'Loading...',
28017     /**
28018      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28019      */
28020     resizable: false,
28021     /**
28022      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28023      */
28024     handleHeight : 8,
28025     /**
28026      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28027      * traditional select (defaults to true)
28028      */
28029     editable: true,
28030     /**
28031      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28032      */
28033     allQuery: '',
28034     /**
28035      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28036      */
28037     mode: 'remote',
28038     /**
28039      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28040      * listWidth has a higher value)
28041      */
28042     minListWidth : 70,
28043     /**
28044      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28045      * allow the user to set arbitrary text into the field (defaults to false)
28046      */
28047     forceSelection:false,
28048     /**
28049      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28050      * if typeAhead = true (defaults to 250)
28051      */
28052     typeAheadDelay : 250,
28053     /**
28054      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28055      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28056      */
28057     valueNotFoundText : undefined,
28058     
28059     /**
28060      * @cfg {String} defaultValue The value displayed after loading the store.
28061      */
28062     defaultValue: '',
28063     
28064     /**
28065      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28066      */
28067     blockFocus : false,
28068     
28069     /**
28070      * @cfg {Boolean} disableClear Disable showing of clear button.
28071      */
28072     disableClear : false,
28073     /**
28074      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28075      */
28076     alwaysQuery : false,
28077     
28078     //private
28079     addicon : false,
28080     editicon: false,
28081     
28082     // element that contains real text value.. (when hidden is used..)
28083      
28084     // private
28085     onRender : function(ct, position){
28086         Roo.form.Field.prototype.onRender.call(this, ct, position);
28087         
28088         if(this.store){
28089             this.store.on('beforeload', this.onBeforeLoad, this);
28090             this.store.on('load', this.onLoad, this);
28091             this.store.on('loadexception', this.onLoadException, this);
28092             this.store.load({});
28093         }
28094         
28095         
28096         
28097     },
28098
28099     // private
28100     initEvents : function(){
28101         //Roo.form.ComboBox.superclass.initEvents.call(this);
28102  
28103     },
28104
28105     onDestroy : function(){
28106        
28107         if(this.store){
28108             this.store.un('beforeload', this.onBeforeLoad, this);
28109             this.store.un('load', this.onLoad, this);
28110             this.store.un('loadexception', this.onLoadException, this);
28111         }
28112         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28113     },
28114
28115     // private
28116     fireKey : function(e){
28117         if(e.isNavKeyPress() && !this.list.isVisible()){
28118             this.fireEvent("specialkey", this, e);
28119         }
28120     },
28121
28122     // private
28123     onResize: function(w, h){
28124         
28125         return; 
28126     
28127         
28128     },
28129
28130     /**
28131      * Allow or prevent the user from directly editing the field text.  If false is passed,
28132      * the user will only be able to select from the items defined in the dropdown list.  This method
28133      * is the runtime equivalent of setting the 'editable' config option at config time.
28134      * @param {Boolean} value True to allow the user to directly edit the field text
28135      */
28136     setEditable : function(value){
28137          
28138     },
28139
28140     // private
28141     onBeforeLoad : function(){
28142         
28143         Roo.log("Select before load");
28144         return;
28145     
28146         this.innerList.update(this.loadingText ?
28147                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28148         //this.restrictHeight();
28149         this.selectedIndex = -1;
28150     },
28151
28152     // private
28153     onLoad : function(){
28154
28155     
28156         var dom = this.el.dom;
28157         dom.innerHTML = '';
28158          var od = dom.ownerDocument;
28159          
28160         if (this.emptyText) {
28161             var op = od.createElement('option');
28162             op.setAttribute('value', '');
28163             op.innerHTML = String.format('{0}', this.emptyText);
28164             dom.appendChild(op);
28165         }
28166         if(this.store.getCount() > 0){
28167            
28168             var vf = this.valueField;
28169             var df = this.displayField;
28170             this.store.data.each(function(r) {
28171                 // which colmsn to use... testing - cdoe / title..
28172                 var op = od.createElement('option');
28173                 op.setAttribute('value', r.data[vf]);
28174                 op.innerHTML = String.format('{0}', r.data[df]);
28175                 dom.appendChild(op);
28176             });
28177             if (typeof(this.defaultValue != 'undefined')) {
28178                 this.setValue(this.defaultValue);
28179             }
28180             
28181              
28182         }else{
28183             //this.onEmptyResults();
28184         }
28185         //this.el.focus();
28186     },
28187     // private
28188     onLoadException : function()
28189     {
28190         dom.innerHTML = '';
28191             
28192         Roo.log("Select on load exception");
28193         return;
28194     
28195         this.collapse();
28196         Roo.log(this.store.reader.jsonData);
28197         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28198             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28199         }
28200         
28201         
28202     },
28203     // private
28204     onTypeAhead : function(){
28205          
28206     },
28207
28208     // private
28209     onSelect : function(record, index){
28210         Roo.log('on select?');
28211         return;
28212         if(this.fireEvent('beforeselect', this, record, index) !== false){
28213             this.setFromData(index > -1 ? record.data : false);
28214             this.collapse();
28215             this.fireEvent('select', this, record, index);
28216         }
28217     },
28218
28219     /**
28220      * Returns the currently selected field value or empty string if no value is set.
28221      * @return {String} value The selected value
28222      */
28223     getValue : function(){
28224         var dom = this.el.dom;
28225         this.value = dom.options[dom.selectedIndex].value;
28226         return this.value;
28227         
28228     },
28229
28230     /**
28231      * Clears any text/value currently set in the field
28232      */
28233     clearValue : function(){
28234         this.value = '';
28235         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28236         
28237     },
28238
28239     /**
28240      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28241      * will be displayed in the field.  If the value does not match the data value of an existing item,
28242      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28243      * Otherwise the field will be blank (although the value will still be set).
28244      * @param {String} value The value to match
28245      */
28246     setValue : function(v){
28247         var d = this.el.dom;
28248         for (var i =0; i < d.options.length;i++) {
28249             if (v == d.options[i].value) {
28250                 d.selectedIndex = i;
28251                 this.value = v;
28252                 return;
28253             }
28254         }
28255         this.clearValue();
28256     },
28257     /**
28258      * @property {Object} the last set data for the element
28259      */
28260     
28261     lastData : false,
28262     /**
28263      * Sets the value of the field based on a object which is related to the record format for the store.
28264      * @param {Object} value the value to set as. or false on reset?
28265      */
28266     setFromData : function(o){
28267         Roo.log('setfrom data?');
28268          
28269         
28270         
28271     },
28272     // private
28273     reset : function(){
28274         this.clearValue();
28275     },
28276     // private
28277     findRecord : function(prop, value){
28278         
28279         return false;
28280     
28281         var record;
28282         if(this.store.getCount() > 0){
28283             this.store.each(function(r){
28284                 if(r.data[prop] == value){
28285                     record = r;
28286                     return false;
28287                 }
28288                 return true;
28289             });
28290         }
28291         return record;
28292     },
28293     
28294     getName: function()
28295     {
28296         // returns hidden if it's set..
28297         if (!this.rendered) {return ''};
28298         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28299         
28300     },
28301      
28302
28303     
28304
28305     // private
28306     onEmptyResults : function(){
28307         Roo.log('empty results');
28308         //this.collapse();
28309     },
28310
28311     /**
28312      * Returns true if the dropdown list is expanded, else false.
28313      */
28314     isExpanded : function(){
28315         return false;
28316     },
28317
28318     /**
28319      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28320      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28321      * @param {String} value The data value of the item to select
28322      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28323      * selected item if it is not currently in view (defaults to true)
28324      * @return {Boolean} True if the value matched an item in the list, else false
28325      */
28326     selectByValue : function(v, scrollIntoView){
28327         Roo.log('select By Value');
28328         return false;
28329     
28330         if(v !== undefined && v !== null){
28331             var r = this.findRecord(this.valueField || this.displayField, v);
28332             if(r){
28333                 this.select(this.store.indexOf(r), scrollIntoView);
28334                 return true;
28335             }
28336         }
28337         return false;
28338     },
28339
28340     /**
28341      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28342      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28343      * @param {Number} index The zero-based index of the list item to select
28344      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28345      * selected item if it is not currently in view (defaults to true)
28346      */
28347     select : function(index, scrollIntoView){
28348         Roo.log('select ');
28349         return  ;
28350         
28351         this.selectedIndex = index;
28352         this.view.select(index);
28353         if(scrollIntoView !== false){
28354             var el = this.view.getNode(index);
28355             if(el){
28356                 this.innerList.scrollChildIntoView(el, false);
28357             }
28358         }
28359     },
28360
28361       
28362
28363     // private
28364     validateBlur : function(){
28365         
28366         return;
28367         
28368     },
28369
28370     // private
28371     initQuery : function(){
28372         this.doQuery(this.getRawValue());
28373     },
28374
28375     // private
28376     doForce : function(){
28377         if(this.el.dom.value.length > 0){
28378             this.el.dom.value =
28379                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28380              
28381         }
28382     },
28383
28384     /**
28385      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28386      * query allowing the query action to be canceled if needed.
28387      * @param {String} query The SQL query to execute
28388      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28389      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28390      * saved in the current store (defaults to false)
28391      */
28392     doQuery : function(q, forceAll){
28393         
28394         Roo.log('doQuery?');
28395         if(q === undefined || q === null){
28396             q = '';
28397         }
28398         var qe = {
28399             query: q,
28400             forceAll: forceAll,
28401             combo: this,
28402             cancel:false
28403         };
28404         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28405             return false;
28406         }
28407         q = qe.query;
28408         forceAll = qe.forceAll;
28409         if(forceAll === true || (q.length >= this.minChars)){
28410             if(this.lastQuery != q || this.alwaysQuery){
28411                 this.lastQuery = q;
28412                 if(this.mode == 'local'){
28413                     this.selectedIndex = -1;
28414                     if(forceAll){
28415                         this.store.clearFilter();
28416                     }else{
28417                         this.store.filter(this.displayField, q);
28418                     }
28419                     this.onLoad();
28420                 }else{
28421                     this.store.baseParams[this.queryParam] = q;
28422                     this.store.load({
28423                         params: this.getParams(q)
28424                     });
28425                     this.expand();
28426                 }
28427             }else{
28428                 this.selectedIndex = -1;
28429                 this.onLoad();   
28430             }
28431         }
28432     },
28433
28434     // private
28435     getParams : function(q){
28436         var p = {};
28437         //p[this.queryParam] = q;
28438         if(this.pageSize){
28439             p.start = 0;
28440             p.limit = this.pageSize;
28441         }
28442         return p;
28443     },
28444
28445     /**
28446      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28447      */
28448     collapse : function(){
28449         
28450     },
28451
28452     // private
28453     collapseIf : function(e){
28454         
28455     },
28456
28457     /**
28458      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28459      */
28460     expand : function(){
28461         
28462     } ,
28463
28464     // private
28465      
28466
28467     /** 
28468     * @cfg {Boolean} grow 
28469     * @hide 
28470     */
28471     /** 
28472     * @cfg {Number} growMin 
28473     * @hide 
28474     */
28475     /** 
28476     * @cfg {Number} growMax 
28477     * @hide 
28478     */
28479     /**
28480      * @hide
28481      * @method autoSize
28482      */
28483     
28484     setWidth : function()
28485     {
28486         
28487     },
28488     getResizeEl : function(){
28489         return this.el;
28490     }
28491 });//<script type="text/javasscript">
28492  
28493
28494 /**
28495  * @class Roo.DDView
28496  * A DnD enabled version of Roo.View.
28497  * @param {Element/String} container The Element in which to create the View.
28498  * @param {String} tpl The template string used to create the markup for each element of the View
28499  * @param {Object} config The configuration properties. These include all the config options of
28500  * {@link Roo.View} plus some specific to this class.<br>
28501  * <p>
28502  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28503  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28504  * <p>
28505  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28506 .x-view-drag-insert-above {
28507         border-top:1px dotted #3366cc;
28508 }
28509 .x-view-drag-insert-below {
28510         border-bottom:1px dotted #3366cc;
28511 }
28512 </code></pre>
28513  * 
28514  */
28515  
28516 Roo.DDView = function(container, tpl, config) {
28517     Roo.DDView.superclass.constructor.apply(this, arguments);
28518     this.getEl().setStyle("outline", "0px none");
28519     this.getEl().unselectable();
28520     if (this.dragGroup) {
28521         this.setDraggable(this.dragGroup.split(","));
28522     }
28523     if (this.dropGroup) {
28524         this.setDroppable(this.dropGroup.split(","));
28525     }
28526     if (this.deletable) {
28527         this.setDeletable();
28528     }
28529     this.isDirtyFlag = false;
28530         this.addEvents({
28531                 "drop" : true
28532         });
28533 };
28534
28535 Roo.extend(Roo.DDView, Roo.View, {
28536 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28537 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28538 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28539 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28540
28541         isFormField: true,
28542
28543         reset: Roo.emptyFn,
28544         
28545         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28546
28547         validate: function() {
28548                 return true;
28549         },
28550         
28551         destroy: function() {
28552                 this.purgeListeners();
28553                 this.getEl.removeAllListeners();
28554                 this.getEl().remove();
28555                 if (this.dragZone) {
28556                         if (this.dragZone.destroy) {
28557                                 this.dragZone.destroy();
28558                         }
28559                 }
28560                 if (this.dropZone) {
28561                         if (this.dropZone.destroy) {
28562                                 this.dropZone.destroy();
28563                         }
28564                 }
28565         },
28566
28567 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28568         getName: function() {
28569                 return this.name;
28570         },
28571
28572 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28573         setValue: function(v) {
28574                 if (!this.store) {
28575                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28576                 }
28577                 var data = {};
28578                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28579                 this.store.proxy = new Roo.data.MemoryProxy(data);
28580                 this.store.load();
28581         },
28582
28583 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28584         getValue: function() {
28585                 var result = '(';
28586                 this.store.each(function(rec) {
28587                         result += rec.id + ',';
28588                 });
28589                 return result.substr(0, result.length - 1) + ')';
28590         },
28591         
28592         getIds: function() {
28593                 var i = 0, result = new Array(this.store.getCount());
28594                 this.store.each(function(rec) {
28595                         result[i++] = rec.id;
28596                 });
28597                 return result;
28598         },
28599         
28600         isDirty: function() {
28601                 return this.isDirtyFlag;
28602         },
28603
28604 /**
28605  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28606  *      whole Element becomes the target, and this causes the drop gesture to append.
28607  */
28608     getTargetFromEvent : function(e) {
28609                 var target = e.getTarget();
28610                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28611                 target = target.parentNode;
28612                 }
28613                 if (!target) {
28614                         target = this.el.dom.lastChild || this.el.dom;
28615                 }
28616                 return target;
28617     },
28618
28619 /**
28620  *      Create the drag data which consists of an object which has the property "ddel" as
28621  *      the drag proxy element. 
28622  */
28623     getDragData : function(e) {
28624         var target = this.findItemFromChild(e.getTarget());
28625                 if(target) {
28626                         this.handleSelection(e);
28627                         var selNodes = this.getSelectedNodes();
28628             var dragData = {
28629                 source: this,
28630                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28631                 nodes: selNodes,
28632                 records: []
28633                         };
28634                         var selectedIndices = this.getSelectedIndexes();
28635                         for (var i = 0; i < selectedIndices.length; i++) {
28636                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28637                         }
28638                         if (selNodes.length == 1) {
28639                                 dragData.ddel = target.cloneNode(true); // the div element
28640                         } else {
28641                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28642                                 div.className = 'multi-proxy';
28643                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28644                                         div.appendChild(selNodes[i].cloneNode(true));
28645                                 }
28646                                 dragData.ddel = div;
28647                         }
28648             //console.log(dragData)
28649             //console.log(dragData.ddel.innerHTML)
28650                         return dragData;
28651                 }
28652         //console.log('nodragData')
28653                 return false;
28654     },
28655     
28656 /**     Specify to which ddGroup items in this DDView may be dragged. */
28657     setDraggable: function(ddGroup) {
28658         if (ddGroup instanceof Array) {
28659                 Roo.each(ddGroup, this.setDraggable, this);
28660                 return;
28661         }
28662         if (this.dragZone) {
28663                 this.dragZone.addToGroup(ddGroup);
28664         } else {
28665                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28666                                 containerScroll: true,
28667                                 ddGroup: ddGroup 
28668
28669                         });
28670 //                      Draggability implies selection. DragZone's mousedown selects the element.
28671                         if (!this.multiSelect) { this.singleSelect = true; }
28672
28673 //                      Wire the DragZone's handlers up to methods in *this*
28674                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28675                 }
28676     },
28677
28678 /**     Specify from which ddGroup this DDView accepts drops. */
28679     setDroppable: function(ddGroup) {
28680         if (ddGroup instanceof Array) {
28681                 Roo.each(ddGroup, this.setDroppable, this);
28682                 return;
28683         }
28684         if (this.dropZone) {
28685                 this.dropZone.addToGroup(ddGroup);
28686         } else {
28687                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28688                                 containerScroll: true,
28689                                 ddGroup: ddGroup
28690                         });
28691
28692 //                      Wire the DropZone's handlers up to methods in *this*
28693                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28694                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28695                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28696                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28697                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28698                 }
28699     },
28700
28701 /**     Decide whether to drop above or below a View node. */
28702     getDropPoint : function(e, n, dd){
28703         if (n == this.el.dom) { return "above"; }
28704                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28705                 var c = t + (b - t) / 2;
28706                 var y = Roo.lib.Event.getPageY(e);
28707                 if(y <= c) {
28708                         return "above";
28709                 }else{
28710                         return "below";
28711                 }
28712     },
28713
28714     onNodeEnter : function(n, dd, e, data){
28715                 return false;
28716     },
28717     
28718     onNodeOver : function(n, dd, e, data){
28719                 var pt = this.getDropPoint(e, n, dd);
28720                 // set the insert point style on the target node
28721                 var dragElClass = this.dropNotAllowed;
28722                 if (pt) {
28723                         var targetElClass;
28724                         if (pt == "above"){
28725                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28726                                 targetElClass = "x-view-drag-insert-above";
28727                         } else {
28728                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28729                                 targetElClass = "x-view-drag-insert-below";
28730                         }
28731                         if (this.lastInsertClass != targetElClass){
28732                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28733                                 this.lastInsertClass = targetElClass;
28734                         }
28735                 }
28736                 return dragElClass;
28737         },
28738
28739     onNodeOut : function(n, dd, e, data){
28740                 this.removeDropIndicators(n);
28741     },
28742
28743     onNodeDrop : function(n, dd, e, data){
28744         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28745                 return false;
28746         }
28747         var pt = this.getDropPoint(e, n, dd);
28748                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28749                 if (pt == "below") { insertAt++; }
28750                 for (var i = 0; i < data.records.length; i++) {
28751                         var r = data.records[i];
28752                         var dup = this.store.getById(r.id);
28753                         if (dup && (dd != this.dragZone)) {
28754                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28755                         } else {
28756                                 if (data.copy) {
28757                                         this.store.insert(insertAt++, r.copy());
28758                                 } else {
28759                                         data.source.isDirtyFlag = true;
28760                                         r.store.remove(r);
28761                                         this.store.insert(insertAt++, r);
28762                                 }
28763                                 this.isDirtyFlag = true;
28764                         }
28765                 }
28766                 this.dragZone.cachedTarget = null;
28767                 return true;
28768     },
28769
28770     removeDropIndicators : function(n){
28771                 if(n){
28772                         Roo.fly(n).removeClass([
28773                                 "x-view-drag-insert-above",
28774                                 "x-view-drag-insert-below"]);
28775                         this.lastInsertClass = "_noclass";
28776                 }
28777     },
28778
28779 /**
28780  *      Utility method. Add a delete option to the DDView's context menu.
28781  *      @param {String} imageUrl The URL of the "delete" icon image.
28782  */
28783         setDeletable: function(imageUrl) {
28784                 if (!this.singleSelect && !this.multiSelect) {
28785                         this.singleSelect = true;
28786                 }
28787                 var c = this.getContextMenu();
28788                 this.contextMenu.on("itemclick", function(item) {
28789                         switch (item.id) {
28790                                 case "delete":
28791                                         this.remove(this.getSelectedIndexes());
28792                                         break;
28793                         }
28794                 }, this);
28795                 this.contextMenu.add({
28796                         icon: imageUrl,
28797                         id: "delete",
28798                         text: 'Delete'
28799                 });
28800         },
28801         
28802 /**     Return the context menu for this DDView. */
28803         getContextMenu: function() {
28804                 if (!this.contextMenu) {
28805 //                      Create the View's context menu
28806                         this.contextMenu = new Roo.menu.Menu({
28807                                 id: this.id + "-contextmenu"
28808                         });
28809                         this.el.on("contextmenu", this.showContextMenu, this);
28810                 }
28811                 return this.contextMenu;
28812         },
28813         
28814         disableContextMenu: function() {
28815                 if (this.contextMenu) {
28816                         this.el.un("contextmenu", this.showContextMenu, this);
28817                 }
28818         },
28819
28820         showContextMenu: function(e, item) {
28821         item = this.findItemFromChild(e.getTarget());
28822                 if (item) {
28823                         e.stopEvent();
28824                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28825                         this.contextMenu.showAt(e.getXY());
28826             }
28827     },
28828
28829 /**
28830  *      Remove {@link Roo.data.Record}s at the specified indices.
28831  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28832  */
28833     remove: function(selectedIndices) {
28834                 selectedIndices = [].concat(selectedIndices);
28835                 for (var i = 0; i < selectedIndices.length; i++) {
28836                         var rec = this.store.getAt(selectedIndices[i]);
28837                         this.store.remove(rec);
28838                 }
28839     },
28840
28841 /**
28842  *      Double click fires the event, but also, if this is draggable, and there is only one other
28843  *      related DropZone, it transfers the selected node.
28844  */
28845     onDblClick : function(e){
28846         var item = this.findItemFromChild(e.getTarget());
28847         if(item){
28848             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28849                 return false;
28850             }
28851             if (this.dragGroup) {
28852                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28853                     while (targets.indexOf(this.dropZone) > -1) {
28854                             targets.remove(this.dropZone);
28855                                 }
28856                     if (targets.length == 1) {
28857                                         this.dragZone.cachedTarget = null;
28858                         var el = Roo.get(targets[0].getEl());
28859                         var box = el.getBox(true);
28860                         targets[0].onNodeDrop(el.dom, {
28861                                 target: el.dom,
28862                                 xy: [box.x, box.y + box.height - 1]
28863                         }, null, this.getDragData(e));
28864                     }
28865                 }
28866         }
28867     },
28868     
28869     handleSelection: function(e) {
28870                 this.dragZone.cachedTarget = null;
28871         var item = this.findItemFromChild(e.getTarget());
28872         if (!item) {
28873                 this.clearSelections(true);
28874                 return;
28875         }
28876                 if (item && (this.multiSelect || this.singleSelect)){
28877                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28878                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28879                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28880                                 this.unselect(item);
28881                         } else {
28882                                 this.select(item, this.multiSelect && e.ctrlKey);
28883                                 this.lastSelection = item;
28884                         }
28885                 }
28886     },
28887
28888     onItemClick : function(item, index, e){
28889                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28890                         return false;
28891                 }
28892                 return true;
28893     },
28894
28895     unselect : function(nodeInfo, suppressEvent){
28896                 var node = this.getNode(nodeInfo);
28897                 if(node && this.isSelected(node)){
28898                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28899                                 Roo.fly(node).removeClass(this.selectedClass);
28900                                 this.selections.remove(node);
28901                                 if(!suppressEvent){
28902                                         this.fireEvent("selectionchange", this, this.selections);
28903                                 }
28904                         }
28905                 }
28906     }
28907 });
28908 /*
28909  * Based on:
28910  * Ext JS Library 1.1.1
28911  * Copyright(c) 2006-2007, Ext JS, LLC.
28912  *
28913  * Originally Released Under LGPL - original licence link has changed is not relivant.
28914  *
28915  * Fork - LGPL
28916  * <script type="text/javascript">
28917  */
28918  
28919 /**
28920  * @class Roo.LayoutManager
28921  * @extends Roo.util.Observable
28922  * Base class for layout managers.
28923  */
28924 Roo.LayoutManager = function(container, config){
28925     Roo.LayoutManager.superclass.constructor.call(this);
28926     this.el = Roo.get(container);
28927     // ie scrollbar fix
28928     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28929         document.body.scroll = "no";
28930     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28931         this.el.position('relative');
28932     }
28933     this.id = this.el.id;
28934     this.el.addClass("x-layout-container");
28935     /** false to disable window resize monitoring @type Boolean */
28936     this.monitorWindowResize = true;
28937     this.regions = {};
28938     this.addEvents({
28939         /**
28940          * @event layout
28941          * Fires when a layout is performed. 
28942          * @param {Roo.LayoutManager} this
28943          */
28944         "layout" : true,
28945         /**
28946          * @event regionresized
28947          * Fires when the user resizes a region. 
28948          * @param {Roo.LayoutRegion} region The resized region
28949          * @param {Number} newSize The new size (width for east/west, height for north/south)
28950          */
28951         "regionresized" : true,
28952         /**
28953          * @event regioncollapsed
28954          * Fires when a region is collapsed. 
28955          * @param {Roo.LayoutRegion} region The collapsed region
28956          */
28957         "regioncollapsed" : true,
28958         /**
28959          * @event regionexpanded
28960          * Fires when a region is expanded.  
28961          * @param {Roo.LayoutRegion} region The expanded region
28962          */
28963         "regionexpanded" : true
28964     });
28965     this.updating = false;
28966     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28967 };
28968
28969 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28970     /**
28971      * Returns true if this layout is currently being updated
28972      * @return {Boolean}
28973      */
28974     isUpdating : function(){
28975         return this.updating; 
28976     },
28977     
28978     /**
28979      * Suspend the LayoutManager from doing auto-layouts while
28980      * making multiple add or remove calls
28981      */
28982     beginUpdate : function(){
28983         this.updating = true;    
28984     },
28985     
28986     /**
28987      * Restore auto-layouts and optionally disable the manager from performing a layout
28988      * @param {Boolean} noLayout true to disable a layout update 
28989      */
28990     endUpdate : function(noLayout){
28991         this.updating = false;
28992         if(!noLayout){
28993             this.layout();
28994         }    
28995     },
28996     
28997     layout: function(){
28998         
28999     },
29000     
29001     onRegionResized : function(region, newSize){
29002         this.fireEvent("regionresized", region, newSize);
29003         this.layout();
29004     },
29005     
29006     onRegionCollapsed : function(region){
29007         this.fireEvent("regioncollapsed", region);
29008     },
29009     
29010     onRegionExpanded : function(region){
29011         this.fireEvent("regionexpanded", region);
29012     },
29013         
29014     /**
29015      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29016      * performs box-model adjustments.
29017      * @return {Object} The size as an object {width: (the width), height: (the height)}
29018      */
29019     getViewSize : function(){
29020         var size;
29021         if(this.el.dom != document.body){
29022             size = this.el.getSize();
29023         }else{
29024             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29025         }
29026         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29027         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29028         return size;
29029     },
29030     
29031     /**
29032      * Returns the Element this layout is bound to.
29033      * @return {Roo.Element}
29034      */
29035     getEl : function(){
29036         return this.el;
29037     },
29038     
29039     /**
29040      * Returns the specified region.
29041      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29042      * @return {Roo.LayoutRegion}
29043      */
29044     getRegion : function(target){
29045         return this.regions[target.toLowerCase()];
29046     },
29047     
29048     onWindowResize : function(){
29049         if(this.monitorWindowResize){
29050             this.layout();
29051         }
29052     }
29053 });/*
29054  * Based on:
29055  * Ext JS Library 1.1.1
29056  * Copyright(c) 2006-2007, Ext JS, LLC.
29057  *
29058  * Originally Released Under LGPL - original licence link has changed is not relivant.
29059  *
29060  * Fork - LGPL
29061  * <script type="text/javascript">
29062  */
29063 /**
29064  * @class Roo.BorderLayout
29065  * @extends Roo.LayoutManager
29066  * @children Roo.ContentPanel
29067  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29068  * please see: <br><br>
29069  * <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>
29070  * <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>
29071  * Example:
29072  <pre><code>
29073  var layout = new Roo.BorderLayout(document.body, {
29074     north: {
29075         initialSize: 25,
29076         titlebar: false
29077     },
29078     west: {
29079         split:true,
29080         initialSize: 200,
29081         minSize: 175,
29082         maxSize: 400,
29083         titlebar: true,
29084         collapsible: true
29085     },
29086     east: {
29087         split:true,
29088         initialSize: 202,
29089         minSize: 175,
29090         maxSize: 400,
29091         titlebar: true,
29092         collapsible: true
29093     },
29094     south: {
29095         split:true,
29096         initialSize: 100,
29097         minSize: 100,
29098         maxSize: 200,
29099         titlebar: true,
29100         collapsible: true
29101     },
29102     center: {
29103         titlebar: true,
29104         autoScroll:true,
29105         resizeTabs: true,
29106         minTabWidth: 50,
29107         preferredTabWidth: 150
29108     }
29109 });
29110
29111 // shorthand
29112 var CP = Roo.ContentPanel;
29113
29114 layout.beginUpdate();
29115 layout.add("north", new CP("north", "North"));
29116 layout.add("south", new CP("south", {title: "South", closable: true}));
29117 layout.add("west", new CP("west", {title: "West"}));
29118 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29119 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29120 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29121 layout.getRegion("center").showPanel("center1");
29122 layout.endUpdate();
29123 </code></pre>
29124
29125 <b>The container the layout is rendered into can be either the body element or any other element.
29126 If it is not the body element, the container needs to either be an absolute positioned element,
29127 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29128 the container size if it is not the body element.</b>
29129
29130 * @constructor
29131 * Create a new BorderLayout
29132 * @param {String/HTMLElement/Element} container The container this layout is bound to
29133 * @param {Object} config Configuration options
29134  */
29135 Roo.BorderLayout = function(container, config){
29136     config = config || {};
29137     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29138     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29139     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29140         var target = this.factory.validRegions[i];
29141         if(config[target]){
29142             this.addRegion(target, config[target]);
29143         }
29144     }
29145 };
29146
29147 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29148         
29149         /**
29150          * @cfg {Roo.LayoutRegion} east
29151          */
29152         /**
29153          * @cfg {Roo.LayoutRegion} west
29154          */
29155         /**
29156          * @cfg {Roo.LayoutRegion} north
29157          */
29158         /**
29159          * @cfg {Roo.LayoutRegion} south
29160          */
29161         /**
29162          * @cfg {Roo.LayoutRegion} center
29163          */
29164     /**
29165      * Creates and adds a new region if it doesn't already exist.
29166      * @param {String} target The target region key (north, south, east, west or center).
29167      * @param {Object} config The regions config object
29168      * @return {BorderLayoutRegion} The new region
29169      */
29170     addRegion : function(target, config){
29171         if(!this.regions[target]){
29172             var r = this.factory.create(target, this, config);
29173             this.bindRegion(target, r);
29174         }
29175         return this.regions[target];
29176     },
29177
29178     // private (kinda)
29179     bindRegion : function(name, r){
29180         this.regions[name] = r;
29181         r.on("visibilitychange", this.layout, this);
29182         r.on("paneladded", this.layout, this);
29183         r.on("panelremoved", this.layout, this);
29184         r.on("invalidated", this.layout, this);
29185         r.on("resized", this.onRegionResized, this);
29186         r.on("collapsed", this.onRegionCollapsed, this);
29187         r.on("expanded", this.onRegionExpanded, this);
29188     },
29189
29190     /**
29191      * Performs a layout update.
29192      */
29193     layout : function(){
29194         if(this.updating) {
29195             return;
29196         }
29197         var size = this.getViewSize();
29198         var w = size.width;
29199         var h = size.height;
29200         var centerW = w;
29201         var centerH = h;
29202         var centerY = 0;
29203         var centerX = 0;
29204         //var x = 0, y = 0;
29205
29206         var rs = this.regions;
29207         var north = rs["north"];
29208         var south = rs["south"]; 
29209         var west = rs["west"];
29210         var east = rs["east"];
29211         var center = rs["center"];
29212         //if(this.hideOnLayout){ // not supported anymore
29213             //c.el.setStyle("display", "none");
29214         //}
29215         if(north && north.isVisible()){
29216             var b = north.getBox();
29217             var m = north.getMargins();
29218             b.width = w - (m.left+m.right);
29219             b.x = m.left;
29220             b.y = m.top;
29221             centerY = b.height + b.y + m.bottom;
29222             centerH -= centerY;
29223             north.updateBox(this.safeBox(b));
29224         }
29225         if(south && south.isVisible()){
29226             var b = south.getBox();
29227             var m = south.getMargins();
29228             b.width = w - (m.left+m.right);
29229             b.x = m.left;
29230             var totalHeight = (b.height + m.top + m.bottom);
29231             b.y = h - totalHeight + m.top;
29232             centerH -= totalHeight;
29233             south.updateBox(this.safeBox(b));
29234         }
29235         if(west && west.isVisible()){
29236             var b = west.getBox();
29237             var m = west.getMargins();
29238             b.height = centerH - (m.top+m.bottom);
29239             b.x = m.left;
29240             b.y = centerY + m.top;
29241             var totalWidth = (b.width + m.left + m.right);
29242             centerX += totalWidth;
29243             centerW -= totalWidth;
29244             west.updateBox(this.safeBox(b));
29245         }
29246         if(east && east.isVisible()){
29247             var b = east.getBox();
29248             var m = east.getMargins();
29249             b.height = centerH - (m.top+m.bottom);
29250             var totalWidth = (b.width + m.left + m.right);
29251             b.x = w - totalWidth + m.left;
29252             b.y = centerY + m.top;
29253             centerW -= totalWidth;
29254             east.updateBox(this.safeBox(b));
29255         }
29256         if(center){
29257             var m = center.getMargins();
29258             var centerBox = {
29259                 x: centerX + m.left,
29260                 y: centerY + m.top,
29261                 width: centerW - (m.left+m.right),
29262                 height: centerH - (m.top+m.bottom)
29263             };
29264             //if(this.hideOnLayout){
29265                 //center.el.setStyle("display", "block");
29266             //}
29267             center.updateBox(this.safeBox(centerBox));
29268         }
29269         this.el.repaint();
29270         this.fireEvent("layout", this);
29271     },
29272
29273     // private
29274     safeBox : function(box){
29275         box.width = Math.max(0, box.width);
29276         box.height = Math.max(0, box.height);
29277         return box;
29278     },
29279
29280     /**
29281      * Adds a ContentPanel (or subclass) to this layout.
29282      * @param {String} target The target region key (north, south, east, west or center).
29283      * @param {Roo.ContentPanel} panel The panel to add
29284      * @return {Roo.ContentPanel} The added panel
29285      */
29286     add : function(target, panel){
29287          
29288         target = target.toLowerCase();
29289         return this.regions[target].add(panel);
29290     },
29291
29292     /**
29293      * Remove a ContentPanel (or subclass) to this layout.
29294      * @param {String} target The target region key (north, south, east, west or center).
29295      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29296      * @return {Roo.ContentPanel} The removed panel
29297      */
29298     remove : function(target, panel){
29299         target = target.toLowerCase();
29300         return this.regions[target].remove(panel);
29301     },
29302
29303     /**
29304      * Searches all regions for a panel with the specified id
29305      * @param {String} panelId
29306      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29307      */
29308     findPanel : function(panelId){
29309         var rs = this.regions;
29310         for(var target in rs){
29311             if(typeof rs[target] != "function"){
29312                 var p = rs[target].getPanel(panelId);
29313                 if(p){
29314                     return p;
29315                 }
29316             }
29317         }
29318         return null;
29319     },
29320
29321     /**
29322      * Searches all regions for a panel with the specified id and activates (shows) it.
29323      * @param {String/ContentPanel} panelId The panels id or the panel itself
29324      * @return {Roo.ContentPanel} The shown panel or null
29325      */
29326     showPanel : function(panelId) {
29327       var rs = this.regions;
29328       for(var target in rs){
29329          var r = rs[target];
29330          if(typeof r != "function"){
29331             if(r.hasPanel(panelId)){
29332                return r.showPanel(panelId);
29333             }
29334          }
29335       }
29336       return null;
29337    },
29338
29339    /**
29340      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29341      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29342      */
29343     restoreState : function(provider){
29344         if(!provider){
29345             provider = Roo.state.Manager;
29346         }
29347         var sm = new Roo.LayoutStateManager();
29348         sm.init(this, provider);
29349     },
29350
29351     /**
29352      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29353      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29354      * a valid ContentPanel config object.  Example:
29355      * <pre><code>
29356 // Create the main layout
29357 var layout = new Roo.BorderLayout('main-ct', {
29358     west: {
29359         split:true,
29360         minSize: 175,
29361         titlebar: true
29362     },
29363     center: {
29364         title:'Components'
29365     }
29366 }, 'main-ct');
29367
29368 // Create and add multiple ContentPanels at once via configs
29369 layout.batchAdd({
29370    west: {
29371        id: 'source-files',
29372        autoCreate:true,
29373        title:'Ext Source Files',
29374        autoScroll:true,
29375        fitToFrame:true
29376    },
29377    center : {
29378        el: cview,
29379        autoScroll:true,
29380        fitToFrame:true,
29381        toolbar: tb,
29382        resizeEl:'cbody'
29383    }
29384 });
29385 </code></pre>
29386      * @param {Object} regions An object containing ContentPanel configs by region name
29387      */
29388     batchAdd : function(regions){
29389         this.beginUpdate();
29390         for(var rname in regions){
29391             var lr = this.regions[rname];
29392             if(lr){
29393                 this.addTypedPanels(lr, regions[rname]);
29394             }
29395         }
29396         this.endUpdate();
29397     },
29398
29399     // private
29400     addTypedPanels : function(lr, ps){
29401         if(typeof ps == 'string'){
29402             lr.add(new Roo.ContentPanel(ps));
29403         }
29404         else if(ps instanceof Array){
29405             for(var i =0, len = ps.length; i < len; i++){
29406                 this.addTypedPanels(lr, ps[i]);
29407             }
29408         }
29409         else if(!ps.events){ // raw config?
29410             var el = ps.el;
29411             delete ps.el; // prevent conflict
29412             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29413         }
29414         else {  // panel object assumed!
29415             lr.add(ps);
29416         }
29417     },
29418     /**
29419      * Adds a xtype elements to the layout.
29420      * <pre><code>
29421
29422 layout.addxtype({
29423        xtype : 'ContentPanel',
29424        region: 'west',
29425        items: [ .... ]
29426    }
29427 );
29428
29429 layout.addxtype({
29430         xtype : 'NestedLayoutPanel',
29431         region: 'west',
29432         layout: {
29433            center: { },
29434            west: { }   
29435         },
29436         items : [ ... list of content panels or nested layout panels.. ]
29437    }
29438 );
29439 </code></pre>
29440      * @param {Object} cfg Xtype definition of item to add.
29441      */
29442     addxtype : function(cfg)
29443     {
29444         // basically accepts a pannel...
29445         // can accept a layout region..!?!?
29446         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29447         
29448         if (!cfg.xtype.match(/Panel$/)) {
29449             return false;
29450         }
29451         var ret = false;
29452         
29453         if (typeof(cfg.region) == 'undefined') {
29454             Roo.log("Failed to add Panel, region was not set");
29455             Roo.log(cfg);
29456             return false;
29457         }
29458         var region = cfg.region;
29459         delete cfg.region;
29460         
29461           
29462         var xitems = [];
29463         if (cfg.items) {
29464             xitems = cfg.items;
29465             delete cfg.items;
29466         }
29467         var nb = false;
29468         
29469         switch(cfg.xtype) 
29470         {
29471             case 'ContentPanel':  // ContentPanel (el, cfg)
29472             case 'ScrollPanel':  // ContentPanel (el, cfg)
29473             case 'ViewPanel': 
29474                 if(cfg.autoCreate) {
29475                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29476                 } else {
29477                     var el = this.el.createChild();
29478                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29479                 }
29480                 
29481                 this.add(region, ret);
29482                 break;
29483             
29484             
29485             case 'TreePanel': // our new panel!
29486                 cfg.el = this.el.createChild();
29487                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29488                 this.add(region, ret);
29489                 break;
29490             
29491             case 'NestedLayoutPanel': 
29492                 // create a new Layout (which is  a Border Layout...
29493                 var el = this.el.createChild();
29494                 var clayout = cfg.layout;
29495                 delete cfg.layout;
29496                 clayout.items   = clayout.items  || [];
29497                 // replace this exitems with the clayout ones..
29498                 xitems = clayout.items;
29499                  
29500                 
29501                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29502                     cfg.background = false;
29503                 }
29504                 var layout = new Roo.BorderLayout(el, clayout);
29505                 
29506                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29507                 //console.log('adding nested layout panel '  + cfg.toSource());
29508                 this.add(region, ret);
29509                 nb = {}; /// find first...
29510                 break;
29511                 
29512             case 'GridPanel': 
29513             
29514                 // needs grid and region
29515                 
29516                 //var el = this.getRegion(region).el.createChild();
29517                 var el = this.el.createChild();
29518                 // create the grid first...
29519                 
29520                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29521                 delete cfg.grid;
29522                 if (region == 'center' && this.active ) {
29523                     cfg.background = false;
29524                 }
29525                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29526                 
29527                 this.add(region, ret);
29528                 if (cfg.background) {
29529                     ret.on('activate', function(gp) {
29530                         if (!gp.grid.rendered) {
29531                             gp.grid.render();
29532                         }
29533                     });
29534                 } else {
29535                     grid.render();
29536                 }
29537                 break;
29538            
29539            
29540            
29541                 
29542                 
29543                 
29544             default:
29545                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29546                     
29547                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29548                     this.add(region, ret);
29549                 } else {
29550                 
29551                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29552                     return null;
29553                 }
29554                 
29555              // GridPanel (grid, cfg)
29556             
29557         }
29558         this.beginUpdate();
29559         // add children..
29560         var region = '';
29561         var abn = {};
29562         Roo.each(xitems, function(i)  {
29563             region = nb && i.region ? i.region : false;
29564             
29565             var add = ret.addxtype(i);
29566            
29567             if (region) {
29568                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29569                 if (!i.background) {
29570                     abn[region] = nb[region] ;
29571                 }
29572             }
29573             
29574         });
29575         this.endUpdate();
29576
29577         // make the last non-background panel active..
29578         //if (nb) { Roo.log(abn); }
29579         if (nb) {
29580             
29581             for(var r in abn) {
29582                 region = this.getRegion(r);
29583                 if (region) {
29584                     // tried using nb[r], but it does not work..
29585                      
29586                     region.showPanel(abn[r]);
29587                    
29588                 }
29589             }
29590         }
29591         return ret;
29592         
29593     }
29594 });
29595
29596 /**
29597  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29598  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29599  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29600  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29601  * <pre><code>
29602 // shorthand
29603 var CP = Roo.ContentPanel;
29604
29605 var layout = Roo.BorderLayout.create({
29606     north: {
29607         initialSize: 25,
29608         titlebar: false,
29609         panels: [new CP("north", "North")]
29610     },
29611     west: {
29612         split:true,
29613         initialSize: 200,
29614         minSize: 175,
29615         maxSize: 400,
29616         titlebar: true,
29617         collapsible: true,
29618         panels: [new CP("west", {title: "West"})]
29619     },
29620     east: {
29621         split:true,
29622         initialSize: 202,
29623         minSize: 175,
29624         maxSize: 400,
29625         titlebar: true,
29626         collapsible: true,
29627         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29628     },
29629     south: {
29630         split:true,
29631         initialSize: 100,
29632         minSize: 100,
29633         maxSize: 200,
29634         titlebar: true,
29635         collapsible: true,
29636         panels: [new CP("south", {title: "South", closable: true})]
29637     },
29638     center: {
29639         titlebar: true,
29640         autoScroll:true,
29641         resizeTabs: true,
29642         minTabWidth: 50,
29643         preferredTabWidth: 150,
29644         panels: [
29645             new CP("center1", {title: "Close Me", closable: true}),
29646             new CP("center2", {title: "Center Panel", closable: false})
29647         ]
29648     }
29649 }, document.body);
29650
29651 layout.getRegion("center").showPanel("center1");
29652 </code></pre>
29653  * @param config
29654  * @param targetEl
29655  */
29656 Roo.BorderLayout.create = function(config, targetEl){
29657     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29658     layout.beginUpdate();
29659     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29660     for(var j = 0, jlen = regions.length; j < jlen; j++){
29661         var lr = regions[j];
29662         if(layout.regions[lr] && config[lr].panels){
29663             var r = layout.regions[lr];
29664             var ps = config[lr].panels;
29665             layout.addTypedPanels(r, ps);
29666         }
29667     }
29668     layout.endUpdate();
29669     return layout;
29670 };
29671
29672 // private
29673 Roo.BorderLayout.RegionFactory = {
29674     // private
29675     validRegions : ["north","south","east","west","center"],
29676
29677     // private
29678     create : function(target, mgr, config){
29679         target = target.toLowerCase();
29680         if(config.lightweight || config.basic){
29681             return new Roo.BasicLayoutRegion(mgr, config, target);
29682         }
29683         switch(target){
29684             case "north":
29685                 return new Roo.NorthLayoutRegion(mgr, config);
29686             case "south":
29687                 return new Roo.SouthLayoutRegion(mgr, config);
29688             case "east":
29689                 return new Roo.EastLayoutRegion(mgr, config);
29690             case "west":
29691                 return new Roo.WestLayoutRegion(mgr, config);
29692             case "center":
29693                 return new Roo.CenterLayoutRegion(mgr, config);
29694         }
29695         throw 'Layout region "'+target+'" not supported.';
29696     }
29697 };/*
29698  * Based on:
29699  * Ext JS Library 1.1.1
29700  * Copyright(c) 2006-2007, Ext JS, LLC.
29701  *
29702  * Originally Released Under LGPL - original licence link has changed is not relivant.
29703  *
29704  * Fork - LGPL
29705  * <script type="text/javascript">
29706  */
29707  
29708 /**
29709  * @class Roo.BasicLayoutRegion
29710  * @extends Roo.util.Observable
29711  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29712  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29713  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29714  */
29715 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29716     this.mgr = mgr;
29717     this.position  = pos;
29718     this.events = {
29719         /**
29720          * @scope Roo.BasicLayoutRegion
29721          */
29722         
29723         /**
29724          * @event beforeremove
29725          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29726          * @param {Roo.LayoutRegion} this
29727          * @param {Roo.ContentPanel} panel The panel
29728          * @param {Object} e The cancel event object
29729          */
29730         "beforeremove" : true,
29731         /**
29732          * @event invalidated
29733          * Fires when the layout for this region is changed.
29734          * @param {Roo.LayoutRegion} this
29735          */
29736         "invalidated" : true,
29737         /**
29738          * @event visibilitychange
29739          * Fires when this region is shown or hidden 
29740          * @param {Roo.LayoutRegion} this
29741          * @param {Boolean} visibility true or false
29742          */
29743         "visibilitychange" : true,
29744         /**
29745          * @event paneladded
29746          * Fires when a panel is added. 
29747          * @param {Roo.LayoutRegion} this
29748          * @param {Roo.ContentPanel} panel The panel
29749          */
29750         "paneladded" : true,
29751         /**
29752          * @event panelremoved
29753          * Fires when a panel is removed. 
29754          * @param {Roo.LayoutRegion} this
29755          * @param {Roo.ContentPanel} panel The panel
29756          */
29757         "panelremoved" : true,
29758         /**
29759          * @event beforecollapse
29760          * Fires when this region before collapse.
29761          * @param {Roo.LayoutRegion} this
29762          */
29763         "beforecollapse" : true,
29764         /**
29765          * @event collapsed
29766          * Fires when this region is collapsed.
29767          * @param {Roo.LayoutRegion} this
29768          */
29769         "collapsed" : true,
29770         /**
29771          * @event expanded
29772          * Fires when this region is expanded.
29773          * @param {Roo.LayoutRegion} this
29774          */
29775         "expanded" : true,
29776         /**
29777          * @event slideshow
29778          * Fires when this region is slid into view.
29779          * @param {Roo.LayoutRegion} this
29780          */
29781         "slideshow" : true,
29782         /**
29783          * @event slidehide
29784          * Fires when this region slides out of view. 
29785          * @param {Roo.LayoutRegion} this
29786          */
29787         "slidehide" : true,
29788         /**
29789          * @event panelactivated
29790          * Fires when a panel is activated. 
29791          * @param {Roo.LayoutRegion} this
29792          * @param {Roo.ContentPanel} panel The activated panel
29793          */
29794         "panelactivated" : true,
29795         /**
29796          * @event resized
29797          * Fires when the user resizes this region. 
29798          * @param {Roo.LayoutRegion} this
29799          * @param {Number} newSize The new size (width for east/west, height for north/south)
29800          */
29801         "resized" : true
29802     };
29803     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29804     this.panels = new Roo.util.MixedCollection();
29805     this.panels.getKey = this.getPanelId.createDelegate(this);
29806     this.box = null;
29807     this.activePanel = null;
29808     // ensure listeners are added...
29809     
29810     if (config.listeners || config.events) {
29811         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29812             listeners : config.listeners || {},
29813             events : config.events || {}
29814         });
29815     }
29816     
29817     if(skipConfig !== true){
29818         this.applyConfig(config);
29819     }
29820 };
29821
29822 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29823     getPanelId : function(p){
29824         return p.getId();
29825     },
29826     
29827     applyConfig : function(config){
29828         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29829         this.config = config;
29830         
29831     },
29832     
29833     /**
29834      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29835      * the width, for horizontal (north, south) the height.
29836      * @param {Number} newSize The new width or height
29837      */
29838     resizeTo : function(newSize){
29839         var el = this.el ? this.el :
29840                  (this.activePanel ? this.activePanel.getEl() : null);
29841         if(el){
29842             switch(this.position){
29843                 case "east":
29844                 case "west":
29845                     el.setWidth(newSize);
29846                     this.fireEvent("resized", this, newSize);
29847                 break;
29848                 case "north":
29849                 case "south":
29850                     el.setHeight(newSize);
29851                     this.fireEvent("resized", this, newSize);
29852                 break;                
29853             }
29854         }
29855     },
29856     
29857     getBox : function(){
29858         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29859     },
29860     
29861     getMargins : function(){
29862         return this.margins;
29863     },
29864     
29865     updateBox : function(box){
29866         this.box = box;
29867         var el = this.activePanel.getEl();
29868         el.dom.style.left = box.x + "px";
29869         el.dom.style.top = box.y + "px";
29870         this.activePanel.setSize(box.width, box.height);
29871     },
29872     
29873     /**
29874      * Returns the container element for this region.
29875      * @return {Roo.Element}
29876      */
29877     getEl : function(){
29878         return this.activePanel;
29879     },
29880     
29881     /**
29882      * Returns true if this region is currently visible.
29883      * @return {Boolean}
29884      */
29885     isVisible : function(){
29886         return this.activePanel ? true : false;
29887     },
29888     
29889     setActivePanel : function(panel){
29890         panel = this.getPanel(panel);
29891         if(this.activePanel && this.activePanel != panel){
29892             this.activePanel.setActiveState(false);
29893             this.activePanel.getEl().setLeftTop(-10000,-10000);
29894         }
29895         this.activePanel = panel;
29896         panel.setActiveState(true);
29897         if(this.box){
29898             panel.setSize(this.box.width, this.box.height);
29899         }
29900         this.fireEvent("panelactivated", this, panel);
29901         this.fireEvent("invalidated");
29902     },
29903     
29904     /**
29905      * Show the specified panel.
29906      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29907      * @return {Roo.ContentPanel} The shown panel or null
29908      */
29909     showPanel : function(panel){
29910         if(panel = this.getPanel(panel)){
29911             this.setActivePanel(panel);
29912         }
29913         return panel;
29914     },
29915     
29916     /**
29917      * Get the active panel for this region.
29918      * @return {Roo.ContentPanel} The active panel or null
29919      */
29920     getActivePanel : function(){
29921         return this.activePanel;
29922     },
29923     
29924     /**
29925      * Add the passed ContentPanel(s)
29926      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29927      * @return {Roo.ContentPanel} The panel added (if only one was added)
29928      */
29929     add : function(panel){
29930         if(arguments.length > 1){
29931             for(var i = 0, len = arguments.length; i < len; i++) {
29932                 this.add(arguments[i]);
29933             }
29934             return null;
29935         }
29936         if(this.hasPanel(panel)){
29937             this.showPanel(panel);
29938             return panel;
29939         }
29940         var el = panel.getEl();
29941         if(el.dom.parentNode != this.mgr.el.dom){
29942             this.mgr.el.dom.appendChild(el.dom);
29943         }
29944         if(panel.setRegion){
29945             panel.setRegion(this);
29946         }
29947         this.panels.add(panel);
29948         el.setStyle("position", "absolute");
29949         if(!panel.background){
29950             this.setActivePanel(panel);
29951             if(this.config.initialSize && this.panels.getCount()==1){
29952                 this.resizeTo(this.config.initialSize);
29953             }
29954         }
29955         this.fireEvent("paneladded", this, panel);
29956         return panel;
29957     },
29958     
29959     /**
29960      * Returns true if the panel is in this region.
29961      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29962      * @return {Boolean}
29963      */
29964     hasPanel : function(panel){
29965         if(typeof panel == "object"){ // must be panel obj
29966             panel = panel.getId();
29967         }
29968         return this.getPanel(panel) ? true : false;
29969     },
29970     
29971     /**
29972      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29973      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29974      * @param {Boolean} preservePanel Overrides the config preservePanel option
29975      * @return {Roo.ContentPanel} The panel that was removed
29976      */
29977     remove : function(panel, preservePanel){
29978         panel = this.getPanel(panel);
29979         if(!panel){
29980             return null;
29981         }
29982         var e = {};
29983         this.fireEvent("beforeremove", this, panel, e);
29984         if(e.cancel === true){
29985             return null;
29986         }
29987         var panelId = panel.getId();
29988         this.panels.removeKey(panelId);
29989         return panel;
29990     },
29991     
29992     /**
29993      * Returns the panel specified or null if it's not in this region.
29994      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29995      * @return {Roo.ContentPanel}
29996      */
29997     getPanel : function(id){
29998         if(typeof id == "object"){ // must be panel obj
29999             return id;
30000         }
30001         return this.panels.get(id);
30002     },
30003     
30004     /**
30005      * Returns this regions position (north/south/east/west/center).
30006      * @return {String} 
30007      */
30008     getPosition: function(){
30009         return this.position;    
30010     }
30011 });/*
30012  * Based on:
30013  * Ext JS Library 1.1.1
30014  * Copyright(c) 2006-2007, Ext JS, LLC.
30015  *
30016  * Originally Released Under LGPL - original licence link has changed is not relivant.
30017  *
30018  * Fork - LGPL
30019  * <script type="text/javascript">
30020  */
30021  
30022 /**
30023  * @class Roo.LayoutRegion
30024  * @extends Roo.BasicLayoutRegion
30025  * This class represents a region in a layout manager.
30026  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30027  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30028  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30029  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30030  * @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})
30031  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30032  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30033  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30034  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30035  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30036  * @cfg {String}    title           The title for the region (overrides panel titles)
30037  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30038  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30039  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30040  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30041  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30042  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30043  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30044  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30045  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30046  * @cfg {Boolean}   showPin         True to show a pin button
30047  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30048  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30049  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30050  * @cfg {Number}    width           For East/West panels
30051  * @cfg {Number}    height          For North/South panels
30052  * @cfg {Boolean}   split           To show the splitter
30053  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30054  */
30055 Roo.LayoutRegion = function(mgr, config, pos){
30056     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30057     var dh = Roo.DomHelper;
30058     /** This region's container element 
30059     * @type Roo.Element */
30060     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30061     /** This region's title element 
30062     * @type Roo.Element */
30063
30064     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30065         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30066         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30067     ]}, true);
30068     this.titleEl.enableDisplayMode();
30069     /** This region's title text element 
30070     * @type HTMLElement */
30071     this.titleTextEl = this.titleEl.dom.firstChild;
30072     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30073     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30074     this.closeBtn.enableDisplayMode();
30075     this.closeBtn.on("click", this.closeClicked, this);
30076     this.closeBtn.hide();
30077
30078     this.createBody(config);
30079     this.visible = true;
30080     this.collapsed = false;
30081
30082     if(config.hideWhenEmpty){
30083         this.hide();
30084         this.on("paneladded", this.validateVisibility, this);
30085         this.on("panelremoved", this.validateVisibility, this);
30086     }
30087     this.applyConfig(config);
30088 };
30089
30090 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30091
30092     createBody : function(){
30093         /** This region's body element 
30094         * @type Roo.Element */
30095         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30096     },
30097
30098     applyConfig : function(c){
30099         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30100             var dh = Roo.DomHelper;
30101             if(c.titlebar !== false){
30102                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30103                 this.collapseBtn.on("click", this.collapse, this);
30104                 this.collapseBtn.enableDisplayMode();
30105
30106                 if(c.showPin === true || this.showPin){
30107                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30108                     this.stickBtn.enableDisplayMode();
30109                     this.stickBtn.on("click", this.expand, this);
30110                     this.stickBtn.hide();
30111                 }
30112             }
30113             /** This region's collapsed element
30114             * @type Roo.Element */
30115             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30116                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30117             ]}, true);
30118             if(c.floatable !== false){
30119                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30120                this.collapsedEl.on("click", this.collapseClick, this);
30121             }
30122
30123             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30124                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30125                    id: "message", unselectable: "on", style:{"float":"left"}});
30126                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30127              }
30128             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30129             this.expandBtn.on("click", this.expand, this);
30130         }
30131         if(this.collapseBtn){
30132             this.collapseBtn.setVisible(c.collapsible == true);
30133         }
30134         this.cmargins = c.cmargins || this.cmargins ||
30135                          (this.position == "west" || this.position == "east" ?
30136                              {top: 0, left: 2, right:2, bottom: 0} :
30137                              {top: 2, left: 0, right:0, bottom: 2});
30138         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30139         this.bottomTabs = c.tabPosition != "top";
30140         this.autoScroll = c.autoScroll || false;
30141         if(this.autoScroll){
30142             this.bodyEl.setStyle("overflow", "auto");
30143         }else{
30144             this.bodyEl.setStyle("overflow", "hidden");
30145         }
30146         //if(c.titlebar !== false){
30147             if((!c.titlebar && !c.title) || c.titlebar === false){
30148                 this.titleEl.hide();
30149             }else{
30150                 this.titleEl.show();
30151                 if(c.title){
30152                     this.titleTextEl.innerHTML = c.title;
30153                 }
30154             }
30155         //}
30156         this.duration = c.duration || .30;
30157         this.slideDuration = c.slideDuration || .45;
30158         this.config = c;
30159         if(c.collapsed){
30160             this.collapse(true);
30161         }
30162         if(c.hidden){
30163             this.hide();
30164         }
30165     },
30166     /**
30167      * Returns true if this region is currently visible.
30168      * @return {Boolean}
30169      */
30170     isVisible : function(){
30171         return this.visible;
30172     },
30173
30174     /**
30175      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30176      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30177      */
30178     setCollapsedTitle : function(title){
30179         title = title || "&#160;";
30180         if(this.collapsedTitleTextEl){
30181             this.collapsedTitleTextEl.innerHTML = title;
30182         }
30183     },
30184
30185     getBox : function(){
30186         var b;
30187         if(!this.collapsed){
30188             b = this.el.getBox(false, true);
30189         }else{
30190             b = this.collapsedEl.getBox(false, true);
30191         }
30192         return b;
30193     },
30194
30195     getMargins : function(){
30196         return this.collapsed ? this.cmargins : this.margins;
30197     },
30198
30199     highlight : function(){
30200         this.el.addClass("x-layout-panel-dragover");
30201     },
30202
30203     unhighlight : function(){
30204         this.el.removeClass("x-layout-panel-dragover");
30205     },
30206
30207     updateBox : function(box){
30208         this.box = box;
30209         if(!this.collapsed){
30210             this.el.dom.style.left = box.x + "px";
30211             this.el.dom.style.top = box.y + "px";
30212             this.updateBody(box.width, box.height);
30213         }else{
30214             this.collapsedEl.dom.style.left = box.x + "px";
30215             this.collapsedEl.dom.style.top = box.y + "px";
30216             this.collapsedEl.setSize(box.width, box.height);
30217         }
30218         if(this.tabs){
30219             this.tabs.autoSizeTabs();
30220         }
30221     },
30222
30223     updateBody : function(w, h){
30224         if(w !== null){
30225             this.el.setWidth(w);
30226             w -= this.el.getBorderWidth("rl");
30227             if(this.config.adjustments){
30228                 w += this.config.adjustments[0];
30229             }
30230         }
30231         if(h !== null){
30232             this.el.setHeight(h);
30233             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30234             h -= this.el.getBorderWidth("tb");
30235             if(this.config.adjustments){
30236                 h += this.config.adjustments[1];
30237             }
30238             this.bodyEl.setHeight(h);
30239             if(this.tabs){
30240                 h = this.tabs.syncHeight(h);
30241             }
30242         }
30243         if(this.panelSize){
30244             w = w !== null ? w : this.panelSize.width;
30245             h = h !== null ? h : this.panelSize.height;
30246         }
30247         if(this.activePanel){
30248             var el = this.activePanel.getEl();
30249             w = w !== null ? w : el.getWidth();
30250             h = h !== null ? h : el.getHeight();
30251             this.panelSize = {width: w, height: h};
30252             this.activePanel.setSize(w, h);
30253         }
30254         if(Roo.isIE && this.tabs){
30255             this.tabs.el.repaint();
30256         }
30257     },
30258
30259     /**
30260      * Returns the container element for this region.
30261      * @return {Roo.Element}
30262      */
30263     getEl : function(){
30264         return this.el;
30265     },
30266
30267     /**
30268      * Hides this region.
30269      */
30270     hide : function(){
30271         if(!this.collapsed){
30272             this.el.dom.style.left = "-2000px";
30273             this.el.hide();
30274         }else{
30275             this.collapsedEl.dom.style.left = "-2000px";
30276             this.collapsedEl.hide();
30277         }
30278         this.visible = false;
30279         this.fireEvent("visibilitychange", this, false);
30280     },
30281
30282     /**
30283      * Shows this region if it was previously hidden.
30284      */
30285     show : function(){
30286         if(!this.collapsed){
30287             this.el.show();
30288         }else{
30289             this.collapsedEl.show();
30290         }
30291         this.visible = true;
30292         this.fireEvent("visibilitychange", this, true);
30293     },
30294
30295     closeClicked : function(){
30296         if(this.activePanel){
30297             this.remove(this.activePanel);
30298         }
30299     },
30300
30301     collapseClick : function(e){
30302         if(this.isSlid){
30303            e.stopPropagation();
30304            this.slideIn();
30305         }else{
30306            e.stopPropagation();
30307            this.slideOut();
30308         }
30309     },
30310
30311     /**
30312      * Collapses this region.
30313      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30314      */
30315     collapse : function(skipAnim, skipCheck){
30316         if(this.collapsed) {
30317             return;
30318         }
30319         
30320         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30321             
30322             this.collapsed = true;
30323             if(this.split){
30324                 this.split.el.hide();
30325             }
30326             if(this.config.animate && skipAnim !== true){
30327                 this.fireEvent("invalidated", this);
30328                 this.animateCollapse();
30329             }else{
30330                 this.el.setLocation(-20000,-20000);
30331                 this.el.hide();
30332                 this.collapsedEl.show();
30333                 this.fireEvent("collapsed", this);
30334                 this.fireEvent("invalidated", this);
30335             }
30336         }
30337         
30338     },
30339
30340     animateCollapse : function(){
30341         // overridden
30342     },
30343
30344     /**
30345      * Expands this region if it was previously collapsed.
30346      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30347      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30348      */
30349     expand : function(e, skipAnim){
30350         if(e) {
30351             e.stopPropagation();
30352         }
30353         if(!this.collapsed || this.el.hasActiveFx()) {
30354             return;
30355         }
30356         if(this.isSlid){
30357             this.afterSlideIn();
30358             skipAnim = true;
30359         }
30360         this.collapsed = false;
30361         if(this.config.animate && skipAnim !== true){
30362             this.animateExpand();
30363         }else{
30364             this.el.show();
30365             if(this.split){
30366                 this.split.el.show();
30367             }
30368             this.collapsedEl.setLocation(-2000,-2000);
30369             this.collapsedEl.hide();
30370             this.fireEvent("invalidated", this);
30371             this.fireEvent("expanded", this);
30372         }
30373     },
30374
30375     animateExpand : function(){
30376         // overridden
30377     },
30378
30379     initTabs : function()
30380     {
30381         this.bodyEl.setStyle("overflow", "hidden");
30382         var ts = new Roo.TabPanel(
30383                 this.bodyEl.dom,
30384                 {
30385                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30386                     disableTooltips: this.config.disableTabTips,
30387                     toolbar : this.config.toolbar
30388                 }
30389         );
30390         if(this.config.hideTabs){
30391             ts.stripWrap.setDisplayed(false);
30392         }
30393         this.tabs = ts;
30394         ts.resizeTabs = this.config.resizeTabs === true;
30395         ts.minTabWidth = this.config.minTabWidth || 40;
30396         ts.maxTabWidth = this.config.maxTabWidth || 250;
30397         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30398         ts.monitorResize = false;
30399         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30400         ts.bodyEl.addClass('x-layout-tabs-body');
30401         this.panels.each(this.initPanelAsTab, this);
30402     },
30403
30404     initPanelAsTab : function(panel){
30405         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30406                     this.config.closeOnTab && panel.isClosable());
30407         if(panel.tabTip !== undefined){
30408             ti.setTooltip(panel.tabTip);
30409         }
30410         ti.on("activate", function(){
30411               this.setActivePanel(panel);
30412         }, this);
30413         if(this.config.closeOnTab){
30414             ti.on("beforeclose", function(t, e){
30415                 e.cancel = true;
30416                 this.remove(panel);
30417             }, this);
30418         }
30419         return ti;
30420     },
30421
30422     updatePanelTitle : function(panel, title){
30423         if(this.activePanel == panel){
30424             this.updateTitle(title);
30425         }
30426         if(this.tabs){
30427             var ti = this.tabs.getTab(panel.getEl().id);
30428             ti.setText(title);
30429             if(panel.tabTip !== undefined){
30430                 ti.setTooltip(panel.tabTip);
30431             }
30432         }
30433     },
30434
30435     updateTitle : function(title){
30436         if(this.titleTextEl && !this.config.title){
30437             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30438         }
30439     },
30440
30441     setActivePanel : function(panel){
30442         panel = this.getPanel(panel);
30443         if(this.activePanel && this.activePanel != panel){
30444             this.activePanel.setActiveState(false);
30445         }
30446         this.activePanel = panel;
30447         panel.setActiveState(true);
30448         if(this.panelSize){
30449             panel.setSize(this.panelSize.width, this.panelSize.height);
30450         }
30451         if(this.closeBtn){
30452             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30453         }
30454         this.updateTitle(panel.getTitle());
30455         if(this.tabs){
30456             this.fireEvent("invalidated", this);
30457         }
30458         this.fireEvent("panelactivated", this, panel);
30459     },
30460
30461     /**
30462      * Shows the specified panel.
30463      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30464      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30465      */
30466     showPanel : function(panel)
30467     {
30468         panel = this.getPanel(panel);
30469         if(panel){
30470             if(this.tabs){
30471                 var tab = this.tabs.getTab(panel.getEl().id);
30472                 if(tab.isHidden()){
30473                     this.tabs.unhideTab(tab.id);
30474                 }
30475                 tab.activate();
30476             }else{
30477                 this.setActivePanel(panel);
30478             }
30479         }
30480         return panel;
30481     },
30482
30483     /**
30484      * Get the active panel for this region.
30485      * @return {Roo.ContentPanel} The active panel or null
30486      */
30487     getActivePanel : function(){
30488         return this.activePanel;
30489     },
30490
30491     validateVisibility : function(){
30492         if(this.panels.getCount() < 1){
30493             this.updateTitle("&#160;");
30494             this.closeBtn.hide();
30495             this.hide();
30496         }else{
30497             if(!this.isVisible()){
30498                 this.show();
30499             }
30500         }
30501     },
30502
30503     /**
30504      * Adds the passed ContentPanel(s) to this region.
30505      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30506      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30507      */
30508     add : function(panel){
30509         if(arguments.length > 1){
30510             for(var i = 0, len = arguments.length; i < len; i++) {
30511                 this.add(arguments[i]);
30512             }
30513             return null;
30514         }
30515         if(this.hasPanel(panel)){
30516             this.showPanel(panel);
30517             return panel;
30518         }
30519         panel.setRegion(this);
30520         this.panels.add(panel);
30521         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30522             this.bodyEl.dom.appendChild(panel.getEl().dom);
30523             if(panel.background !== true){
30524                 this.setActivePanel(panel);
30525             }
30526             this.fireEvent("paneladded", this, panel);
30527             return panel;
30528         }
30529         if(!this.tabs){
30530             this.initTabs();
30531         }else{
30532             this.initPanelAsTab(panel);
30533         }
30534         if(panel.background !== true){
30535             this.tabs.activate(panel.getEl().id);
30536         }
30537         this.fireEvent("paneladded", this, panel);
30538         return panel;
30539     },
30540
30541     /**
30542      * Hides the tab for the specified panel.
30543      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30544      */
30545     hidePanel : function(panel){
30546         if(this.tabs && (panel = this.getPanel(panel))){
30547             this.tabs.hideTab(panel.getEl().id);
30548         }
30549     },
30550
30551     /**
30552      * Unhides the tab for a previously hidden panel.
30553      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30554      */
30555     unhidePanel : function(panel){
30556         if(this.tabs && (panel = this.getPanel(panel))){
30557             this.tabs.unhideTab(panel.getEl().id);
30558         }
30559     },
30560
30561     clearPanels : function(){
30562         while(this.panels.getCount() > 0){
30563              this.remove(this.panels.first());
30564         }
30565     },
30566
30567     /**
30568      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30569      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30570      * @param {Boolean} preservePanel Overrides the config preservePanel option
30571      * @return {Roo.ContentPanel} The panel that was removed
30572      */
30573     remove : function(panel, preservePanel){
30574         panel = this.getPanel(panel);
30575         if(!panel){
30576             return null;
30577         }
30578         var e = {};
30579         this.fireEvent("beforeremove", this, panel, e);
30580         if(e.cancel === true){
30581             return null;
30582         }
30583         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30584         var panelId = panel.getId();
30585         this.panels.removeKey(panelId);
30586         if(preservePanel){
30587             document.body.appendChild(panel.getEl().dom);
30588         }
30589         if(this.tabs){
30590             this.tabs.removeTab(panel.getEl().id);
30591         }else if (!preservePanel){
30592             this.bodyEl.dom.removeChild(panel.getEl().dom);
30593         }
30594         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30595             var p = this.panels.first();
30596             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30597             tempEl.appendChild(p.getEl().dom);
30598             this.bodyEl.update("");
30599             this.bodyEl.dom.appendChild(p.getEl().dom);
30600             tempEl = null;
30601             this.updateTitle(p.getTitle());
30602             this.tabs = null;
30603             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30604             this.setActivePanel(p);
30605         }
30606         panel.setRegion(null);
30607         if(this.activePanel == panel){
30608             this.activePanel = null;
30609         }
30610         if(this.config.autoDestroy !== false && preservePanel !== true){
30611             try{panel.destroy();}catch(e){}
30612         }
30613         this.fireEvent("panelremoved", this, panel);
30614         return panel;
30615     },
30616
30617     /**
30618      * Returns the TabPanel component used by this region
30619      * @return {Roo.TabPanel}
30620      */
30621     getTabs : function(){
30622         return this.tabs;
30623     },
30624
30625     createTool : function(parentEl, className){
30626         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30627             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30628         btn.addClassOnOver("x-layout-tools-button-over");
30629         return btn;
30630     }
30631 });/*
30632  * Based on:
30633  * Ext JS Library 1.1.1
30634  * Copyright(c) 2006-2007, Ext JS, LLC.
30635  *
30636  * Originally Released Under LGPL - original licence link has changed is not relivant.
30637  *
30638  * Fork - LGPL
30639  * <script type="text/javascript">
30640  */
30641  
30642
30643
30644 /**
30645  * @class Roo.SplitLayoutRegion
30646  * @extends Roo.LayoutRegion
30647  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30648  */
30649 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30650     this.cursor = cursor;
30651     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30652 };
30653
30654 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30655     splitTip : "Drag to resize.",
30656     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30657     useSplitTips : false,
30658
30659     applyConfig : function(config){
30660         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30661         if(config.split){
30662             if(!this.split){
30663                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30664                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30665                 /** The SplitBar for this region 
30666                 * @type Roo.SplitBar */
30667                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30668                 this.split.on("moved", this.onSplitMove, this);
30669                 this.split.useShim = config.useShim === true;
30670                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30671                 if(this.useSplitTips){
30672                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30673                 }
30674                 if(config.collapsible){
30675                     this.split.el.on("dblclick", this.collapse,  this);
30676                 }
30677             }
30678             if(typeof config.minSize != "undefined"){
30679                 this.split.minSize = config.minSize;
30680             }
30681             if(typeof config.maxSize != "undefined"){
30682                 this.split.maxSize = config.maxSize;
30683             }
30684             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30685                 this.hideSplitter();
30686             }
30687         }
30688     },
30689
30690     getHMaxSize : function(){
30691          var cmax = this.config.maxSize || 10000;
30692          var center = this.mgr.getRegion("center");
30693          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30694     },
30695
30696     getVMaxSize : function(){
30697          var cmax = this.config.maxSize || 10000;
30698          var center = this.mgr.getRegion("center");
30699          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30700     },
30701
30702     onSplitMove : function(split, newSize){
30703         this.fireEvent("resized", this, newSize);
30704     },
30705     
30706     /** 
30707      * Returns the {@link Roo.SplitBar} for this region.
30708      * @return {Roo.SplitBar}
30709      */
30710     getSplitBar : function(){
30711         return this.split;
30712     },
30713     
30714     hide : function(){
30715         this.hideSplitter();
30716         Roo.SplitLayoutRegion.superclass.hide.call(this);
30717     },
30718
30719     hideSplitter : function(){
30720         if(this.split){
30721             this.split.el.setLocation(-2000,-2000);
30722             this.split.el.hide();
30723         }
30724     },
30725
30726     show : function(){
30727         if(this.split){
30728             this.split.el.show();
30729         }
30730         Roo.SplitLayoutRegion.superclass.show.call(this);
30731     },
30732     
30733     beforeSlide: function(){
30734         if(Roo.isGecko){// firefox overflow auto bug workaround
30735             this.bodyEl.clip();
30736             if(this.tabs) {
30737                 this.tabs.bodyEl.clip();
30738             }
30739             if(this.activePanel){
30740                 this.activePanel.getEl().clip();
30741                 
30742                 if(this.activePanel.beforeSlide){
30743                     this.activePanel.beforeSlide();
30744                 }
30745             }
30746         }
30747     },
30748     
30749     afterSlide : function(){
30750         if(Roo.isGecko){// firefox overflow auto bug workaround
30751             this.bodyEl.unclip();
30752             if(this.tabs) {
30753                 this.tabs.bodyEl.unclip();
30754             }
30755             if(this.activePanel){
30756                 this.activePanel.getEl().unclip();
30757                 if(this.activePanel.afterSlide){
30758                     this.activePanel.afterSlide();
30759                 }
30760             }
30761         }
30762     },
30763
30764     initAutoHide : function(){
30765         if(this.autoHide !== false){
30766             if(!this.autoHideHd){
30767                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30768                 this.autoHideHd = {
30769                     "mouseout": function(e){
30770                         if(!e.within(this.el, true)){
30771                             st.delay(500);
30772                         }
30773                     },
30774                     "mouseover" : function(e){
30775                         st.cancel();
30776                     },
30777                     scope : this
30778                 };
30779             }
30780             this.el.on(this.autoHideHd);
30781         }
30782     },
30783
30784     clearAutoHide : function(){
30785         if(this.autoHide !== false){
30786             this.el.un("mouseout", this.autoHideHd.mouseout);
30787             this.el.un("mouseover", this.autoHideHd.mouseover);
30788         }
30789     },
30790
30791     clearMonitor : function(){
30792         Roo.get(document).un("click", this.slideInIf, this);
30793     },
30794
30795     // these names are backwards but not changed for compat
30796     slideOut : function(){
30797         if(this.isSlid || this.el.hasActiveFx()){
30798             return;
30799         }
30800         this.isSlid = true;
30801         if(this.collapseBtn){
30802             this.collapseBtn.hide();
30803         }
30804         this.closeBtnState = this.closeBtn.getStyle('display');
30805         this.closeBtn.hide();
30806         if(this.stickBtn){
30807             this.stickBtn.show();
30808         }
30809         this.el.show();
30810         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30811         this.beforeSlide();
30812         this.el.setStyle("z-index", 10001);
30813         this.el.slideIn(this.getSlideAnchor(), {
30814             callback: function(){
30815                 this.afterSlide();
30816                 this.initAutoHide();
30817                 Roo.get(document).on("click", this.slideInIf, this);
30818                 this.fireEvent("slideshow", this);
30819             },
30820             scope: this,
30821             block: true
30822         });
30823     },
30824
30825     afterSlideIn : function(){
30826         this.clearAutoHide();
30827         this.isSlid = false;
30828         this.clearMonitor();
30829         this.el.setStyle("z-index", "");
30830         if(this.collapseBtn){
30831             this.collapseBtn.show();
30832         }
30833         this.closeBtn.setStyle('display', this.closeBtnState);
30834         if(this.stickBtn){
30835             this.stickBtn.hide();
30836         }
30837         this.fireEvent("slidehide", this);
30838     },
30839
30840     slideIn : function(cb){
30841         if(!this.isSlid || this.el.hasActiveFx()){
30842             Roo.callback(cb);
30843             return;
30844         }
30845         this.isSlid = false;
30846         this.beforeSlide();
30847         this.el.slideOut(this.getSlideAnchor(), {
30848             callback: function(){
30849                 this.el.setLeftTop(-10000, -10000);
30850                 this.afterSlide();
30851                 this.afterSlideIn();
30852                 Roo.callback(cb);
30853             },
30854             scope: this,
30855             block: true
30856         });
30857     },
30858     
30859     slideInIf : function(e){
30860         if(!e.within(this.el)){
30861             this.slideIn();
30862         }
30863     },
30864
30865     animateCollapse : function(){
30866         this.beforeSlide();
30867         this.el.setStyle("z-index", 20000);
30868         var anchor = this.getSlideAnchor();
30869         this.el.slideOut(anchor, {
30870             callback : function(){
30871                 this.el.setStyle("z-index", "");
30872                 this.collapsedEl.slideIn(anchor, {duration:.3});
30873                 this.afterSlide();
30874                 this.el.setLocation(-10000,-10000);
30875                 this.el.hide();
30876                 this.fireEvent("collapsed", this);
30877             },
30878             scope: this,
30879             block: true
30880         });
30881     },
30882
30883     animateExpand : function(){
30884         this.beforeSlide();
30885         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30886         this.el.setStyle("z-index", 20000);
30887         this.collapsedEl.hide({
30888             duration:.1
30889         });
30890         this.el.slideIn(this.getSlideAnchor(), {
30891             callback : function(){
30892                 this.el.setStyle("z-index", "");
30893                 this.afterSlide();
30894                 if(this.split){
30895                     this.split.el.show();
30896                 }
30897                 this.fireEvent("invalidated", this);
30898                 this.fireEvent("expanded", this);
30899             },
30900             scope: this,
30901             block: true
30902         });
30903     },
30904
30905     anchors : {
30906         "west" : "left",
30907         "east" : "right",
30908         "north" : "top",
30909         "south" : "bottom"
30910     },
30911
30912     sanchors : {
30913         "west" : "l",
30914         "east" : "r",
30915         "north" : "t",
30916         "south" : "b"
30917     },
30918
30919     canchors : {
30920         "west" : "tl-tr",
30921         "east" : "tr-tl",
30922         "north" : "tl-bl",
30923         "south" : "bl-tl"
30924     },
30925
30926     getAnchor : function(){
30927         return this.anchors[this.position];
30928     },
30929
30930     getCollapseAnchor : function(){
30931         return this.canchors[this.position];
30932     },
30933
30934     getSlideAnchor : function(){
30935         return this.sanchors[this.position];
30936     },
30937
30938     getAlignAdj : function(){
30939         var cm = this.cmargins;
30940         switch(this.position){
30941             case "west":
30942                 return [0, 0];
30943             break;
30944             case "east":
30945                 return [0, 0];
30946             break;
30947             case "north":
30948                 return [0, 0];
30949             break;
30950             case "south":
30951                 return [0, 0];
30952             break;
30953         }
30954     },
30955
30956     getExpandAdj : function(){
30957         var c = this.collapsedEl, cm = this.cmargins;
30958         switch(this.position){
30959             case "west":
30960                 return [-(cm.right+c.getWidth()+cm.left), 0];
30961             break;
30962             case "east":
30963                 return [cm.right+c.getWidth()+cm.left, 0];
30964             break;
30965             case "north":
30966                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30967             break;
30968             case "south":
30969                 return [0, cm.top+cm.bottom+c.getHeight()];
30970             break;
30971         }
30972     }
30973 });/*
30974  * Based on:
30975  * Ext JS Library 1.1.1
30976  * Copyright(c) 2006-2007, Ext JS, LLC.
30977  *
30978  * Originally Released Under LGPL - original licence link has changed is not relivant.
30979  *
30980  * Fork - LGPL
30981  * <script type="text/javascript">
30982  */
30983 /*
30984  * These classes are private internal classes
30985  */
30986 Roo.CenterLayoutRegion = function(mgr, config){
30987     Roo.LayoutRegion.call(this, mgr, config, "center");
30988     this.visible = true;
30989     this.minWidth = config.minWidth || 20;
30990     this.minHeight = config.minHeight || 20;
30991 };
30992
30993 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30994     hide : function(){
30995         // center panel can't be hidden
30996     },
30997     
30998     show : function(){
30999         // center panel can't be hidden
31000     },
31001     
31002     getMinWidth: function(){
31003         return this.minWidth;
31004     },
31005     
31006     getMinHeight: function(){
31007         return this.minHeight;
31008     }
31009 });
31010
31011
31012 Roo.NorthLayoutRegion = function(mgr, config){
31013     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31014     if(this.split){
31015         this.split.placement = Roo.SplitBar.TOP;
31016         this.split.orientation = Roo.SplitBar.VERTICAL;
31017         this.split.el.addClass("x-layout-split-v");
31018     }
31019     var size = config.initialSize || config.height;
31020     if(typeof size != "undefined"){
31021         this.el.setHeight(size);
31022     }
31023 };
31024 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31025     orientation: Roo.SplitBar.VERTICAL,
31026     getBox : function(){
31027         if(this.collapsed){
31028             return this.collapsedEl.getBox();
31029         }
31030         var box = this.el.getBox();
31031         if(this.split){
31032             box.height += this.split.el.getHeight();
31033         }
31034         return box;
31035     },
31036     
31037     updateBox : function(box){
31038         if(this.split && !this.collapsed){
31039             box.height -= this.split.el.getHeight();
31040             this.split.el.setLeft(box.x);
31041             this.split.el.setTop(box.y+box.height);
31042             this.split.el.setWidth(box.width);
31043         }
31044         if(this.collapsed){
31045             this.updateBody(box.width, null);
31046         }
31047         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31048     }
31049 });
31050
31051 Roo.SouthLayoutRegion = function(mgr, config){
31052     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31053     if(this.split){
31054         this.split.placement = Roo.SplitBar.BOTTOM;
31055         this.split.orientation = Roo.SplitBar.VERTICAL;
31056         this.split.el.addClass("x-layout-split-v");
31057     }
31058     var size = config.initialSize || config.height;
31059     if(typeof size != "undefined"){
31060         this.el.setHeight(size);
31061     }
31062 };
31063 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31064     orientation: Roo.SplitBar.VERTICAL,
31065     getBox : function(){
31066         if(this.collapsed){
31067             return this.collapsedEl.getBox();
31068         }
31069         var box = this.el.getBox();
31070         if(this.split){
31071             var sh = this.split.el.getHeight();
31072             box.height += sh;
31073             box.y -= sh;
31074         }
31075         return box;
31076     },
31077     
31078     updateBox : function(box){
31079         if(this.split && !this.collapsed){
31080             var sh = this.split.el.getHeight();
31081             box.height -= sh;
31082             box.y += sh;
31083             this.split.el.setLeft(box.x);
31084             this.split.el.setTop(box.y-sh);
31085             this.split.el.setWidth(box.width);
31086         }
31087         if(this.collapsed){
31088             this.updateBody(box.width, null);
31089         }
31090         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31091     }
31092 });
31093
31094 Roo.EastLayoutRegion = function(mgr, config){
31095     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31096     if(this.split){
31097         this.split.placement = Roo.SplitBar.RIGHT;
31098         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31099         this.split.el.addClass("x-layout-split-h");
31100     }
31101     var size = config.initialSize || config.width;
31102     if(typeof size != "undefined"){
31103         this.el.setWidth(size);
31104     }
31105 };
31106 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31107     orientation: Roo.SplitBar.HORIZONTAL,
31108     getBox : function(){
31109         if(this.collapsed){
31110             return this.collapsedEl.getBox();
31111         }
31112         var box = this.el.getBox();
31113         if(this.split){
31114             var sw = this.split.el.getWidth();
31115             box.width += sw;
31116             box.x -= sw;
31117         }
31118         return box;
31119     },
31120
31121     updateBox : function(box){
31122         if(this.split && !this.collapsed){
31123             var sw = this.split.el.getWidth();
31124             box.width -= sw;
31125             this.split.el.setLeft(box.x);
31126             this.split.el.setTop(box.y);
31127             this.split.el.setHeight(box.height);
31128             box.x += sw;
31129         }
31130         if(this.collapsed){
31131             this.updateBody(null, box.height);
31132         }
31133         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31134     }
31135 });
31136
31137 Roo.WestLayoutRegion = function(mgr, config){
31138     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31139     if(this.split){
31140         this.split.placement = Roo.SplitBar.LEFT;
31141         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31142         this.split.el.addClass("x-layout-split-h");
31143     }
31144     var size = config.initialSize || config.width;
31145     if(typeof size != "undefined"){
31146         this.el.setWidth(size);
31147     }
31148 };
31149 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31150     orientation: Roo.SplitBar.HORIZONTAL,
31151     getBox : function(){
31152         if(this.collapsed){
31153             return this.collapsedEl.getBox();
31154         }
31155         var box = this.el.getBox();
31156         if(this.split){
31157             box.width += this.split.el.getWidth();
31158         }
31159         return box;
31160     },
31161     
31162     updateBox : function(box){
31163         if(this.split && !this.collapsed){
31164             var sw = this.split.el.getWidth();
31165             box.width -= sw;
31166             this.split.el.setLeft(box.x+box.width);
31167             this.split.el.setTop(box.y);
31168             this.split.el.setHeight(box.height);
31169         }
31170         if(this.collapsed){
31171             this.updateBody(null, box.height);
31172         }
31173         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31174     }
31175 });
31176 /*
31177  * Based on:
31178  * Ext JS Library 1.1.1
31179  * Copyright(c) 2006-2007, Ext JS, LLC.
31180  *
31181  * Originally Released Under LGPL - original licence link has changed is not relivant.
31182  *
31183  * Fork - LGPL
31184  * <script type="text/javascript">
31185  */
31186  
31187  
31188 /*
31189  * Private internal class for reading and applying state
31190  */
31191 Roo.LayoutStateManager = function(layout){
31192      // default empty state
31193      this.state = {
31194         north: {},
31195         south: {},
31196         east: {},
31197         west: {}       
31198     };
31199 };
31200
31201 Roo.LayoutStateManager.prototype = {
31202     init : function(layout, provider){
31203         this.provider = provider;
31204         var state = provider.get(layout.id+"-layout-state");
31205         if(state){
31206             var wasUpdating = layout.isUpdating();
31207             if(!wasUpdating){
31208                 layout.beginUpdate();
31209             }
31210             for(var key in state){
31211                 if(typeof state[key] != "function"){
31212                     var rstate = state[key];
31213                     var r = layout.getRegion(key);
31214                     if(r && rstate){
31215                         if(rstate.size){
31216                             r.resizeTo(rstate.size);
31217                         }
31218                         if(rstate.collapsed == true){
31219                             r.collapse(true);
31220                         }else{
31221                             r.expand(null, true);
31222                         }
31223                     }
31224                 }
31225             }
31226             if(!wasUpdating){
31227                 layout.endUpdate();
31228             }
31229             this.state = state; 
31230         }
31231         this.layout = layout;
31232         layout.on("regionresized", this.onRegionResized, this);
31233         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31234         layout.on("regionexpanded", this.onRegionExpanded, this);
31235     },
31236     
31237     storeState : function(){
31238         this.provider.set(this.layout.id+"-layout-state", this.state);
31239     },
31240     
31241     onRegionResized : function(region, newSize){
31242         this.state[region.getPosition()].size = newSize;
31243         this.storeState();
31244     },
31245     
31246     onRegionCollapsed : function(region){
31247         this.state[region.getPosition()].collapsed = true;
31248         this.storeState();
31249     },
31250     
31251     onRegionExpanded : function(region){
31252         this.state[region.getPosition()].collapsed = false;
31253         this.storeState();
31254     }
31255 };/*
31256  * Based on:
31257  * Ext JS Library 1.1.1
31258  * Copyright(c) 2006-2007, Ext JS, LLC.
31259  *
31260  * Originally Released Under LGPL - original licence link has changed is not relivant.
31261  *
31262  * Fork - LGPL
31263  * <script type="text/javascript">
31264  */
31265 /**
31266  * @class Roo.ContentPanel
31267  * @extends Roo.util.Observable
31268  * @children Roo.form.Form Roo.JsonView Roo.View
31269  * A basic ContentPanel element.
31270  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31271  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31272  * @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
31273  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31274  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31275  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31276  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31277  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31278  * @cfg {String} title          The title for this panel
31279  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31280  * @cfg {String} url            Calls {@link #setUrl} with this value
31281  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31282  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31283  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31284  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31285  * @cfg {String}    style  Extra style to add to the content panel 
31286
31287  * @constructor
31288  * Create a new ContentPanel.
31289  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31290  * @param {String/Object} config A string to set only the title or a config object
31291  * @param {String} content (optional) Set the HTML content for this panel
31292  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31293  */
31294 Roo.ContentPanel = function(el, config, content){
31295     
31296      
31297     /*
31298     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31299         config = el;
31300         el = Roo.id();
31301     }
31302     if (config && config.parentLayout) { 
31303         el = config.parentLayout.el.createChild(); 
31304     }
31305     */
31306     if(el.autoCreate){ // xtype is available if this is called from factory
31307         config = el;
31308         el = Roo.id();
31309     }
31310     this.el = Roo.get(el);
31311     if(!this.el && config && config.autoCreate){
31312         if(typeof config.autoCreate == "object"){
31313             if(!config.autoCreate.id){
31314                 config.autoCreate.id = config.id||el;
31315             }
31316             this.el = Roo.DomHelper.append(document.body,
31317                         config.autoCreate, true);
31318         }else{
31319             this.el = Roo.DomHelper.append(document.body,
31320                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31321         }
31322     }
31323     
31324     
31325     this.closable = false;
31326     this.loaded = false;
31327     this.active = false;
31328     if(typeof config == "string"){
31329         this.title = config;
31330     }else{
31331         Roo.apply(this, config);
31332     }
31333     
31334     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31335         this.wrapEl = this.el.wrap();
31336         this.toolbar.container = this.el.insertSibling(false, 'before');
31337         this.toolbar = new Roo.Toolbar(this.toolbar);
31338     }
31339     
31340     // xtype created footer. - not sure if will work as we normally have to render first..
31341     if (this.footer && !this.footer.el && this.footer.xtype) {
31342         if (!this.wrapEl) {
31343             this.wrapEl = this.el.wrap();
31344         }
31345     
31346         this.footer.container = this.wrapEl.createChild();
31347          
31348         this.footer = Roo.factory(this.footer, Roo);
31349         
31350     }
31351     
31352     if(this.resizeEl){
31353         this.resizeEl = Roo.get(this.resizeEl, true);
31354     }else{
31355         this.resizeEl = this.el;
31356     }
31357     // handle view.xtype
31358     
31359  
31360     
31361     
31362     this.addEvents({
31363         /**
31364          * @event activate
31365          * Fires when this panel is activated. 
31366          * @param {Roo.ContentPanel} this
31367          */
31368         "activate" : true,
31369         /**
31370          * @event deactivate
31371          * Fires when this panel is activated. 
31372          * @param {Roo.ContentPanel} this
31373          */
31374         "deactivate" : true,
31375
31376         /**
31377          * @event resize
31378          * Fires when this panel is resized if fitToFrame is true.
31379          * @param {Roo.ContentPanel} this
31380          * @param {Number} width The width after any component adjustments
31381          * @param {Number} height The height after any component adjustments
31382          */
31383         "resize" : true,
31384         
31385          /**
31386          * @event render
31387          * Fires when this tab is created
31388          * @param {Roo.ContentPanel} this
31389          */
31390         "render" : true
31391          
31392         
31393     });
31394     
31395
31396     
31397     
31398     if(this.autoScroll){
31399         this.resizeEl.setStyle("overflow", "auto");
31400     } else {
31401         // fix randome scrolling
31402         this.el.on('scroll', function() {
31403             Roo.log('fix random scolling');
31404             this.scrollTo('top',0); 
31405         });
31406     }
31407     content = content || this.content;
31408     if(content){
31409         this.setContent(content);
31410     }
31411     if(config && config.url){
31412         this.setUrl(this.url, this.params, this.loadOnce);
31413     }
31414     
31415     
31416     
31417     Roo.ContentPanel.superclass.constructor.call(this);
31418     
31419     if (this.view && typeof(this.view.xtype) != 'undefined') {
31420         this.view.el = this.el.appendChild(document.createElement("div"));
31421         this.view = Roo.factory(this.view); 
31422         this.view.render  &&  this.view.render(false, '');  
31423     }
31424     
31425     
31426     this.fireEvent('render', this);
31427 };
31428
31429 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31430     tabTip:'',
31431     setRegion : function(region){
31432         this.region = region;
31433         if(region){
31434            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31435         }else{
31436            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31437         } 
31438     },
31439     
31440     /**
31441      * Returns the toolbar for this Panel if one was configured. 
31442      * @return {Roo.Toolbar} 
31443      */
31444     getToolbar : function(){
31445         return this.toolbar;
31446     },
31447     
31448     setActiveState : function(active){
31449         this.active = active;
31450         if(!active){
31451             this.fireEvent("deactivate", this);
31452         }else{
31453             this.fireEvent("activate", this);
31454         }
31455     },
31456     /**
31457      * Updates this panel's element
31458      * @param {String} content The new content
31459      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31460     */
31461     setContent : function(content, loadScripts){
31462         this.el.update(content, loadScripts);
31463     },
31464
31465     ignoreResize : function(w, h){
31466         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31467             return true;
31468         }else{
31469             this.lastSize = {width: w, height: h};
31470             return false;
31471         }
31472     },
31473     /**
31474      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31475      * @return {Roo.UpdateManager} The UpdateManager
31476      */
31477     getUpdateManager : function(){
31478         return this.el.getUpdateManager();
31479     },
31480      /**
31481      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31482      * @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:
31483 <pre><code>
31484 panel.load({
31485     url: "your-url.php",
31486     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31487     callback: yourFunction,
31488     scope: yourObject, //(optional scope)
31489     discardUrl: false,
31490     nocache: false,
31491     text: "Loading...",
31492     timeout: 30,
31493     scripts: false
31494 });
31495 </code></pre>
31496      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31497      * 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.
31498      * @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}
31499      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31500      * @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.
31501      * @return {Roo.ContentPanel} this
31502      */
31503     load : function(){
31504         var um = this.el.getUpdateManager();
31505         um.update.apply(um, arguments);
31506         return this;
31507     },
31508
31509
31510     /**
31511      * 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.
31512      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31513      * @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)
31514      * @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)
31515      * @return {Roo.UpdateManager} The UpdateManager
31516      */
31517     setUrl : function(url, params, loadOnce){
31518         if(this.refreshDelegate){
31519             this.removeListener("activate", this.refreshDelegate);
31520         }
31521         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31522         this.on("activate", this.refreshDelegate);
31523         return this.el.getUpdateManager();
31524     },
31525     
31526     _handleRefresh : function(url, params, loadOnce){
31527         if(!loadOnce || !this.loaded){
31528             var updater = this.el.getUpdateManager();
31529             updater.update(url, params, this._setLoaded.createDelegate(this));
31530         }
31531     },
31532     
31533     _setLoaded : function(){
31534         this.loaded = true;
31535     }, 
31536     
31537     /**
31538      * Returns this panel's id
31539      * @return {String} 
31540      */
31541     getId : function(){
31542         return this.el.id;
31543     },
31544     
31545     /** 
31546      * Returns this panel's element - used by regiosn to add.
31547      * @return {Roo.Element} 
31548      */
31549     getEl : function(){
31550         return this.wrapEl || this.el;
31551     },
31552     
31553     adjustForComponents : function(width, height)
31554     {
31555         //Roo.log('adjustForComponents ');
31556         if(this.resizeEl != this.el){
31557             width -= this.el.getFrameWidth('lr');
31558             height -= this.el.getFrameWidth('tb');
31559         }
31560         if(this.toolbar){
31561             var te = this.toolbar.getEl();
31562             height -= te.getHeight();
31563             te.setWidth(width);
31564         }
31565         if(this.footer){
31566             var te = this.footer.getEl();
31567             //Roo.log("footer:" + te.getHeight());
31568             
31569             height -= te.getHeight();
31570             te.setWidth(width);
31571         }
31572         
31573         
31574         if(this.adjustments){
31575             width += this.adjustments[0];
31576             height += this.adjustments[1];
31577         }
31578         return {"width": width, "height": height};
31579     },
31580     
31581     setSize : function(width, height){
31582         if(this.fitToFrame && !this.ignoreResize(width, height)){
31583             if(this.fitContainer && this.resizeEl != this.el){
31584                 this.el.setSize(width, height);
31585             }
31586             var size = this.adjustForComponents(width, height);
31587             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31588             this.fireEvent('resize', this, size.width, size.height);
31589         }
31590     },
31591     
31592     /**
31593      * Returns this panel's title
31594      * @return {String} 
31595      */
31596     getTitle : function(){
31597         return this.title;
31598     },
31599     
31600     /**
31601      * Set this panel's title
31602      * @param {String} title
31603      */
31604     setTitle : function(title){
31605         this.title = title;
31606         if(this.region){
31607             this.region.updatePanelTitle(this, title);
31608         }
31609     },
31610     
31611     /**
31612      * Returns true is this panel was configured to be closable
31613      * @return {Boolean} 
31614      */
31615     isClosable : function(){
31616         return this.closable;
31617     },
31618     
31619     beforeSlide : function(){
31620         this.el.clip();
31621         this.resizeEl.clip();
31622     },
31623     
31624     afterSlide : function(){
31625         this.el.unclip();
31626         this.resizeEl.unclip();
31627     },
31628     
31629     /**
31630      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31631      *   Will fail silently if the {@link #setUrl} method has not been called.
31632      *   This does not activate the panel, just updates its content.
31633      */
31634     refresh : function(){
31635         if(this.refreshDelegate){
31636            this.loaded = false;
31637            this.refreshDelegate();
31638         }
31639     },
31640     
31641     /**
31642      * Destroys this panel
31643      */
31644     destroy : function(){
31645         this.el.removeAllListeners();
31646         var tempEl = document.createElement("span");
31647         tempEl.appendChild(this.el.dom);
31648         tempEl.innerHTML = "";
31649         this.el.remove();
31650         this.el = null;
31651     },
31652     
31653     /**
31654      * form - if the content panel contains a form - this is a reference to it.
31655      * @type {Roo.form.Form}
31656      */
31657     form : false,
31658     /**
31659      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31660      *    This contains a reference to it.
31661      * @type {Roo.View}
31662      */
31663     view : false,
31664     
31665       /**
31666      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31667      * <pre><code>
31668
31669 layout.addxtype({
31670        xtype : 'Form',
31671        items: [ .... ]
31672    }
31673 );
31674
31675 </code></pre>
31676      * @param {Object} cfg Xtype definition of item to add.
31677      */
31678     
31679     addxtype : function(cfg) {
31680         // add form..
31681         if (cfg.xtype.match(/^Form$/)) {
31682             
31683             var el;
31684             //if (this.footer) {
31685             //    el = this.footer.container.insertSibling(false, 'before');
31686             //} else {
31687                 el = this.el.createChild();
31688             //}
31689
31690             this.form = new  Roo.form.Form(cfg);
31691             
31692             
31693             if ( this.form.allItems.length) {
31694                 this.form.render(el.dom);
31695             }
31696             return this.form;
31697         }
31698         // should only have one of theses..
31699         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31700             // views.. should not be just added - used named prop 'view''
31701             
31702             cfg.el = this.el.appendChild(document.createElement("div"));
31703             // factory?
31704             
31705             var ret = new Roo.factory(cfg);
31706              
31707              ret.render && ret.render(false, ''); // render blank..
31708             this.view = ret;
31709             return ret;
31710         }
31711         return false;
31712     }
31713 });
31714
31715 /**
31716  * @class Roo.GridPanel
31717  * @extends Roo.ContentPanel
31718  * @constructor
31719  * Create a new GridPanel.
31720  * @param {Roo.grid.Grid} grid The grid for this panel
31721  * @param {String/Object} config A string to set only the panel's title, or a config object
31722  */
31723 Roo.GridPanel = function(grid, config){
31724     
31725   
31726     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31727         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31728         
31729     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31730     
31731     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31732     
31733     if(this.toolbar){
31734         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31735     }
31736     // xtype created footer. - not sure if will work as we normally have to render first..
31737     if (this.footer && !this.footer.el && this.footer.xtype) {
31738         
31739         this.footer.container = this.grid.getView().getFooterPanel(true);
31740         this.footer.dataSource = this.grid.dataSource;
31741         this.footer = Roo.factory(this.footer, Roo);
31742         
31743     }
31744     
31745     grid.monitorWindowResize = false; // turn off autosizing
31746     grid.autoHeight = false;
31747     grid.autoWidth = false;
31748     this.grid = grid;
31749     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31750 };
31751
31752 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31753     getId : function(){
31754         return this.grid.id;
31755     },
31756     
31757     /**
31758      * Returns the grid for this panel
31759      * @return {Roo.grid.Grid} 
31760      */
31761     getGrid : function(){
31762         return this.grid;    
31763     },
31764     
31765     setSize : function(width, height){
31766         if(!this.ignoreResize(width, height)){
31767             var grid = this.grid;
31768             var size = this.adjustForComponents(width, height);
31769             grid.getGridEl().setSize(size.width, size.height);
31770             grid.autoSize();
31771         }
31772     },
31773     
31774     beforeSlide : function(){
31775         this.grid.getView().scroller.clip();
31776     },
31777     
31778     afterSlide : function(){
31779         this.grid.getView().scroller.unclip();
31780     },
31781     
31782     destroy : function(){
31783         this.grid.destroy();
31784         delete this.grid;
31785         Roo.GridPanel.superclass.destroy.call(this); 
31786     }
31787 });
31788
31789
31790 /**
31791  * @class Roo.NestedLayoutPanel
31792  * @extends Roo.ContentPanel
31793  * @constructor
31794  * Create a new NestedLayoutPanel.
31795  * 
31796  * 
31797  * @param {Roo.BorderLayout} layout [required] The layout for this panel
31798  * @param {String/Object} config A string to set only the title or a config object
31799  */
31800 Roo.NestedLayoutPanel = function(layout, config)
31801 {
31802     // construct with only one argument..
31803     /* FIXME - implement nicer consturctors
31804     if (layout.layout) {
31805         config = layout;
31806         layout = config.layout;
31807         delete config.layout;
31808     }
31809     if (layout.xtype && !layout.getEl) {
31810         // then layout needs constructing..
31811         layout = Roo.factory(layout, Roo);
31812     }
31813     */
31814     
31815     
31816     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31817     
31818     layout.monitorWindowResize = false; // turn off autosizing
31819     this.layout = layout;
31820     this.layout.getEl().addClass("x-layout-nested-layout");
31821     
31822     
31823     
31824     
31825 };
31826
31827 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31828
31829     setSize : function(width, height){
31830         if(!this.ignoreResize(width, height)){
31831             var size = this.adjustForComponents(width, height);
31832             var el = this.layout.getEl();
31833             el.setSize(size.width, size.height);
31834             var touch = el.dom.offsetWidth;
31835             this.layout.layout();
31836             // ie requires a double layout on the first pass
31837             if(Roo.isIE && !this.initialized){
31838                 this.initialized = true;
31839                 this.layout.layout();
31840             }
31841         }
31842     },
31843     
31844     // activate all subpanels if not currently active..
31845     
31846     setActiveState : function(active){
31847         this.active = active;
31848         if(!active){
31849             this.fireEvent("deactivate", this);
31850             return;
31851         }
31852         
31853         this.fireEvent("activate", this);
31854         // not sure if this should happen before or after..
31855         if (!this.layout) {
31856             return; // should not happen..
31857         }
31858         var reg = false;
31859         for (var r in this.layout.regions) {
31860             reg = this.layout.getRegion(r);
31861             if (reg.getActivePanel()) {
31862                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31863                 reg.setActivePanel(reg.getActivePanel());
31864                 continue;
31865             }
31866             if (!reg.panels.length) {
31867                 continue;
31868             }
31869             reg.showPanel(reg.getPanel(0));
31870         }
31871         
31872         
31873         
31874         
31875     },
31876     
31877     /**
31878      * Returns the nested BorderLayout for this panel
31879      * @return {Roo.BorderLayout} 
31880      */
31881     getLayout : function(){
31882         return this.layout;
31883     },
31884     
31885      /**
31886      * Adds a xtype elements to the layout of the nested panel
31887      * <pre><code>
31888
31889 panel.addxtype({
31890        xtype : 'ContentPanel',
31891        region: 'west',
31892        items: [ .... ]
31893    }
31894 );
31895
31896 panel.addxtype({
31897         xtype : 'NestedLayoutPanel',
31898         region: 'west',
31899         layout: {
31900            center: { },
31901            west: { }   
31902         },
31903         items : [ ... list of content panels or nested layout panels.. ]
31904    }
31905 );
31906 </code></pre>
31907      * @param {Object} cfg Xtype definition of item to add.
31908      */
31909     addxtype : function(cfg) {
31910         return this.layout.addxtype(cfg);
31911     
31912     }
31913 });
31914
31915 Roo.ScrollPanel = function(el, config, content){
31916     config = config || {};
31917     config.fitToFrame = true;
31918     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31919     
31920     this.el.dom.style.overflow = "hidden";
31921     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31922     this.el.removeClass("x-layout-inactive-content");
31923     this.el.on("mousewheel", this.onWheel, this);
31924
31925     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31926     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31927     up.unselectable(); down.unselectable();
31928     up.on("click", this.scrollUp, this);
31929     down.on("click", this.scrollDown, this);
31930     up.addClassOnOver("x-scroller-btn-over");
31931     down.addClassOnOver("x-scroller-btn-over");
31932     up.addClassOnClick("x-scroller-btn-click");
31933     down.addClassOnClick("x-scroller-btn-click");
31934     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31935
31936     this.resizeEl = this.el;
31937     this.el = wrap; this.up = up; this.down = down;
31938 };
31939
31940 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31941     increment : 100,
31942     wheelIncrement : 5,
31943     scrollUp : function(){
31944         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31945     },
31946
31947     scrollDown : function(){
31948         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31949     },
31950
31951     afterScroll : function(){
31952         var el = this.resizeEl;
31953         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31954         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31955         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31956     },
31957
31958     setSize : function(){
31959         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31960         this.afterScroll();
31961     },
31962
31963     onWheel : function(e){
31964         var d = e.getWheelDelta();
31965         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31966         this.afterScroll();
31967         e.stopEvent();
31968     },
31969
31970     setContent : function(content, loadScripts){
31971         this.resizeEl.update(content, loadScripts);
31972     }
31973
31974 });
31975
31976
31977
31978 /**
31979  * @class Roo.TreePanel
31980  * @extends Roo.ContentPanel
31981  * Treepanel component
31982  * 
31983  * @constructor
31984  * Create a new TreePanel. - defaults to fit/scoll contents.
31985  * @param {String/Object} config A string to set only the panel's title, or a config object
31986  */
31987 Roo.TreePanel = function(config){
31988     var el = config.el;
31989     var tree = config.tree;
31990     delete config.tree; 
31991     delete config.el; // hopefull!
31992     
31993     // wrapper for IE7 strict & safari scroll issue
31994     
31995     var treeEl = el.createChild();
31996     config.resizeEl = treeEl;
31997     
31998     
31999     
32000     Roo.TreePanel.superclass.constructor.call(this, el, config);
32001  
32002  
32003     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32004     //console.log(tree);
32005     this.on('activate', function()
32006     {
32007         if (this.tree.rendered) {
32008             return;
32009         }
32010         //console.log('render tree');
32011         this.tree.render();
32012     });
32013     // this should not be needed.. - it's actually the 'el' that resizes?
32014     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32015     
32016     //this.on('resize',  function (cp, w, h) {
32017     //        this.tree.innerCt.setWidth(w);
32018     //        this.tree.innerCt.setHeight(h);
32019     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32020     //});
32021
32022         
32023     
32024 };
32025
32026 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32027     fitToFrame : true,
32028     autoScroll : true,
32029     /*
32030      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32031      */
32032     tree : false
32033
32034 });
32035
32036
32037
32038
32039
32040
32041
32042
32043
32044
32045
32046 /*
32047  * Based on:
32048  * Ext JS Library 1.1.1
32049  * Copyright(c) 2006-2007, Ext JS, LLC.
32050  *
32051  * Originally Released Under LGPL - original licence link has changed is not relivant.
32052  *
32053  * Fork - LGPL
32054  * <script type="text/javascript">
32055  */
32056  
32057
32058 /**
32059  * @class Roo.ReaderLayout
32060  * @extends Roo.BorderLayout
32061  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32062  * center region containing two nested regions (a top one for a list view and one for item preview below),
32063  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32064  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32065  * expedites the setup of the overall layout and regions for this common application style.
32066  * Example:
32067  <pre><code>
32068 var reader = new Roo.ReaderLayout();
32069 var CP = Roo.ContentPanel;  // shortcut for adding
32070
32071 reader.beginUpdate();
32072 reader.add("north", new CP("north", "North"));
32073 reader.add("west", new CP("west", {title: "West"}));
32074 reader.add("east", new CP("east", {title: "East"}));
32075
32076 reader.regions.listView.add(new CP("listView", "List"));
32077 reader.regions.preview.add(new CP("preview", "Preview"));
32078 reader.endUpdate();
32079 </code></pre>
32080 * @constructor
32081 * Create a new ReaderLayout
32082 * @param {Object} config Configuration options
32083 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32084 * document.body if omitted)
32085 */
32086 Roo.ReaderLayout = function(config, renderTo){
32087     var c = config || {size:{}};
32088     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32089         north: c.north !== false ? Roo.apply({
32090             split:false,
32091             initialSize: 32,
32092             titlebar: false
32093         }, c.north) : false,
32094         west: c.west !== false ? Roo.apply({
32095             split:true,
32096             initialSize: 200,
32097             minSize: 175,
32098             maxSize: 400,
32099             titlebar: true,
32100             collapsible: true,
32101             animate: true,
32102             margins:{left:5,right:0,bottom:5,top:5},
32103             cmargins:{left:5,right:5,bottom:5,top:5}
32104         }, c.west) : false,
32105         east: c.east !== false ? Roo.apply({
32106             split:true,
32107             initialSize: 200,
32108             minSize: 175,
32109             maxSize: 400,
32110             titlebar: true,
32111             collapsible: true,
32112             animate: true,
32113             margins:{left:0,right:5,bottom:5,top:5},
32114             cmargins:{left:5,right:5,bottom:5,top:5}
32115         }, c.east) : false,
32116         center: Roo.apply({
32117             tabPosition: 'top',
32118             autoScroll:false,
32119             closeOnTab: true,
32120             titlebar:false,
32121             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32122         }, c.center)
32123     });
32124
32125     this.el.addClass('x-reader');
32126
32127     this.beginUpdate();
32128
32129     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32130         south: c.preview !== false ? Roo.apply({
32131             split:true,
32132             initialSize: 200,
32133             minSize: 100,
32134             autoScroll:true,
32135             collapsible:true,
32136             titlebar: true,
32137             cmargins:{top:5,left:0, right:0, bottom:0}
32138         }, c.preview) : false,
32139         center: Roo.apply({
32140             autoScroll:false,
32141             titlebar:false,
32142             minHeight:200
32143         }, c.listView)
32144     });
32145     this.add('center', new Roo.NestedLayoutPanel(inner,
32146             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32147
32148     this.endUpdate();
32149
32150     this.regions.preview = inner.getRegion('south');
32151     this.regions.listView = inner.getRegion('center');
32152 };
32153
32154 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32155  * Based on:
32156  * Ext JS Library 1.1.1
32157  * Copyright(c) 2006-2007, Ext JS, LLC.
32158  *
32159  * Originally Released Under LGPL - original licence link has changed is not relivant.
32160  *
32161  * Fork - LGPL
32162  * <script type="text/javascript">
32163  */
32164  
32165 /**
32166  * @class Roo.grid.Grid
32167  * @extends Roo.util.Observable
32168  * This class represents the primary interface of a component based grid control.
32169  * <br><br>Usage:<pre><code>
32170  var grid = new Roo.grid.Grid("my-container-id", {
32171      ds: myDataStore,
32172      cm: myColModel,
32173      selModel: mySelectionModel,
32174      autoSizeColumns: true,
32175      monitorWindowResize: false,
32176      trackMouseOver: true
32177  });
32178  // set any options
32179  grid.render();
32180  * </code></pre>
32181  * <b>Common Problems:</b><br/>
32182  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32183  * element will correct this<br/>
32184  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32185  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32186  * are unpredictable.<br/>
32187  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32188  * grid to calculate dimensions/offsets.<br/>
32189   * @constructor
32190  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32191  * The container MUST have some type of size defined for the grid to fill. The container will be
32192  * automatically set to position relative if it isn't already.
32193  * @param {Object} config A config object that sets properties on this grid.
32194  */
32195 Roo.grid.Grid = function(container, config){
32196         // initialize the container
32197         this.container = Roo.get(container);
32198         this.container.update("");
32199         this.container.setStyle("overflow", "hidden");
32200     this.container.addClass('x-grid-container');
32201
32202     this.id = this.container.id;
32203
32204     Roo.apply(this, config);
32205     // check and correct shorthanded configs
32206     if(this.ds){
32207         this.dataSource = this.ds;
32208         delete this.ds;
32209     }
32210     if(this.cm){
32211         this.colModel = this.cm;
32212         delete this.cm;
32213     }
32214     if(this.sm){
32215         this.selModel = this.sm;
32216         delete this.sm;
32217     }
32218
32219     if (this.selModel) {
32220         this.selModel = Roo.factory(this.selModel, Roo.grid);
32221         this.sm = this.selModel;
32222         this.sm.xmodule = this.xmodule || false;
32223     }
32224     if (typeof(this.colModel.config) == 'undefined') {
32225         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32226         this.cm = this.colModel;
32227         this.cm.xmodule = this.xmodule || false;
32228     }
32229     if (this.dataSource) {
32230         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32231         this.ds = this.dataSource;
32232         this.ds.xmodule = this.xmodule || false;
32233          
32234     }
32235     
32236     
32237     
32238     if(this.width){
32239         this.container.setWidth(this.width);
32240     }
32241
32242     if(this.height){
32243         this.container.setHeight(this.height);
32244     }
32245     /** @private */
32246         this.addEvents({
32247         // raw events
32248         /**
32249          * @event click
32250          * The raw click event for the entire grid.
32251          * @param {Roo.EventObject} e
32252          */
32253         "click" : true,
32254         /**
32255          * @event dblclick
32256          * The raw dblclick event for the entire grid.
32257          * @param {Roo.EventObject} e
32258          */
32259         "dblclick" : true,
32260         /**
32261          * @event contextmenu
32262          * The raw contextmenu event for the entire grid.
32263          * @param {Roo.EventObject} e
32264          */
32265         "contextmenu" : true,
32266         /**
32267          * @event mousedown
32268          * The raw mousedown event for the entire grid.
32269          * @param {Roo.EventObject} e
32270          */
32271         "mousedown" : true,
32272         /**
32273          * @event mouseup
32274          * The raw mouseup event for the entire grid.
32275          * @param {Roo.EventObject} e
32276          */
32277         "mouseup" : true,
32278         /**
32279          * @event mouseover
32280          * The raw mouseover event for the entire grid.
32281          * @param {Roo.EventObject} e
32282          */
32283         "mouseover" : true,
32284         /**
32285          * @event mouseout
32286          * The raw mouseout event for the entire grid.
32287          * @param {Roo.EventObject} e
32288          */
32289         "mouseout" : true,
32290         /**
32291          * @event keypress
32292          * The raw keypress event for the entire grid.
32293          * @param {Roo.EventObject} e
32294          */
32295         "keypress" : true,
32296         /**
32297          * @event keydown
32298          * The raw keydown event for the entire grid.
32299          * @param {Roo.EventObject} e
32300          */
32301         "keydown" : true,
32302
32303         // custom events
32304
32305         /**
32306          * @event cellclick
32307          * Fires when a cell is clicked
32308          * @param {Grid} this
32309          * @param {Number} rowIndex
32310          * @param {Number} columnIndex
32311          * @param {Roo.EventObject} e
32312          */
32313         "cellclick" : true,
32314         /**
32315          * @event celldblclick
32316          * Fires when a cell is double clicked
32317          * @param {Grid} this
32318          * @param {Number} rowIndex
32319          * @param {Number} columnIndex
32320          * @param {Roo.EventObject} e
32321          */
32322         "celldblclick" : true,
32323         /**
32324          * @event rowclick
32325          * Fires when a row is clicked
32326          * @param {Grid} this
32327          * @param {Number} rowIndex
32328          * @param {Roo.EventObject} e
32329          */
32330         "rowclick" : true,
32331         /**
32332          * @event rowdblclick
32333          * Fires when a row is double clicked
32334          * @param {Grid} this
32335          * @param {Number} rowIndex
32336          * @param {Roo.EventObject} e
32337          */
32338         "rowdblclick" : true,
32339         /**
32340          * @event headerclick
32341          * Fires when a header is clicked
32342          * @param {Grid} this
32343          * @param {Number} columnIndex
32344          * @param {Roo.EventObject} e
32345          */
32346         "headerclick" : true,
32347         /**
32348          * @event headerdblclick
32349          * Fires when a header cell is double clicked
32350          * @param {Grid} this
32351          * @param {Number} columnIndex
32352          * @param {Roo.EventObject} e
32353          */
32354         "headerdblclick" : true,
32355         /**
32356          * @event rowcontextmenu
32357          * Fires when a row is right clicked
32358          * @param {Grid} this
32359          * @param {Number} rowIndex
32360          * @param {Roo.EventObject} e
32361          */
32362         "rowcontextmenu" : true,
32363         /**
32364          * @event cellcontextmenu
32365          * Fires when a cell is right clicked
32366          * @param {Grid} this
32367          * @param {Number} rowIndex
32368          * @param {Number} cellIndex
32369          * @param {Roo.EventObject} e
32370          */
32371          "cellcontextmenu" : true,
32372         /**
32373          * @event headercontextmenu
32374          * Fires when a header is right clicked
32375          * @param {Grid} this
32376          * @param {Number} columnIndex
32377          * @param {Roo.EventObject} e
32378          */
32379         "headercontextmenu" : true,
32380         /**
32381          * @event bodyscroll
32382          * Fires when the body element is scrolled
32383          * @param {Number} scrollLeft
32384          * @param {Number} scrollTop
32385          */
32386         "bodyscroll" : true,
32387         /**
32388          * @event columnresize
32389          * Fires when the user resizes a column
32390          * @param {Number} columnIndex
32391          * @param {Number} newSize
32392          */
32393         "columnresize" : true,
32394         /**
32395          * @event columnmove
32396          * Fires when the user moves a column
32397          * @param {Number} oldIndex
32398          * @param {Number} newIndex
32399          */
32400         "columnmove" : true,
32401         /**
32402          * @event startdrag
32403          * Fires when row(s) start being dragged
32404          * @param {Grid} this
32405          * @param {Roo.GridDD} dd The drag drop object
32406          * @param {event} e The raw browser event
32407          */
32408         "startdrag" : true,
32409         /**
32410          * @event enddrag
32411          * Fires when a drag operation is complete
32412          * @param {Grid} this
32413          * @param {Roo.GridDD} dd The drag drop object
32414          * @param {event} e The raw browser event
32415          */
32416         "enddrag" : true,
32417         /**
32418          * @event dragdrop
32419          * Fires when dragged row(s) are dropped on a valid DD target
32420          * @param {Grid} this
32421          * @param {Roo.GridDD} dd The drag drop object
32422          * @param {String} targetId The target drag drop object
32423          * @param {event} e The raw browser event
32424          */
32425         "dragdrop" : true,
32426         /**
32427          * @event dragover
32428          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32429          * @param {Grid} this
32430          * @param {Roo.GridDD} dd The drag drop object
32431          * @param {String} targetId The target drag drop object
32432          * @param {event} e The raw browser event
32433          */
32434         "dragover" : true,
32435         /**
32436          * @event dragenter
32437          *  Fires when the dragged row(s) first cross another DD target while being dragged
32438          * @param {Grid} this
32439          * @param {Roo.GridDD} dd The drag drop object
32440          * @param {String} targetId The target drag drop object
32441          * @param {event} e The raw browser event
32442          */
32443         "dragenter" : true,
32444         /**
32445          * @event dragout
32446          * Fires when the dragged row(s) leave another DD target while being dragged
32447          * @param {Grid} this
32448          * @param {Roo.GridDD} dd The drag drop object
32449          * @param {String} targetId The target drag drop object
32450          * @param {event} e The raw browser event
32451          */
32452         "dragout" : true,
32453         /**
32454          * @event rowclass
32455          * Fires when a row is rendered, so you can change add a style to it.
32456          * @param {GridView} gridview   The grid view
32457          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32458          */
32459         'rowclass' : true,
32460
32461         /**
32462          * @event render
32463          * Fires when the grid is rendered
32464          * @param {Grid} grid
32465          */
32466         'render' : true
32467     });
32468
32469     Roo.grid.Grid.superclass.constructor.call(this);
32470 };
32471 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32472     
32473     /**
32474      * @cfg {String} ddGroup - drag drop group.
32475      */
32476       /**
32477      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32478      */
32479
32480     /**
32481      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32482      */
32483     minColumnWidth : 25,
32484
32485     /**
32486      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32487      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32488      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32489      */
32490     autoSizeColumns : false,
32491
32492     /**
32493      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32494      */
32495     autoSizeHeaders : true,
32496
32497     /**
32498      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32499      */
32500     monitorWindowResize : true,
32501
32502     /**
32503      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32504      * rows measured to get a columns size. Default is 0 (all rows).
32505      */
32506     maxRowsToMeasure : 0,
32507
32508     /**
32509      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32510      */
32511     trackMouseOver : true,
32512
32513     /**
32514     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32515     */
32516       /**
32517     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32518     */
32519     
32520     /**
32521     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32522     */
32523     enableDragDrop : false,
32524     
32525     /**
32526     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32527     */
32528     enableColumnMove : true,
32529     
32530     /**
32531     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32532     */
32533     enableColumnHide : true,
32534     
32535     /**
32536     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32537     */
32538     enableRowHeightSync : false,
32539     
32540     /**
32541     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32542     */
32543     stripeRows : true,
32544     
32545     /**
32546     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32547     */
32548     autoHeight : false,
32549
32550     /**
32551      * @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.
32552      */
32553     autoExpandColumn : false,
32554
32555     /**
32556     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32557     * Default is 50.
32558     */
32559     autoExpandMin : 50,
32560
32561     /**
32562     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32563     */
32564     autoExpandMax : 1000,
32565
32566     /**
32567     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32568     */
32569     view : null,
32570
32571     /**
32572     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32573     */
32574     loadMask : false,
32575     /**
32576     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32577     */
32578     dropTarget: false,
32579     
32580    
32581     
32582     // private
32583     rendered : false,
32584
32585     /**
32586     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32587     * of a fixed width. Default is false.
32588     */
32589     /**
32590     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32591     */
32592     
32593     
32594     /**
32595     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32596     * %0 is replaced with the number of selected rows.
32597     */
32598     ddText : "{0} selected row{1}",
32599     
32600     
32601     /**
32602      * Called once after all setup has been completed and the grid is ready to be rendered.
32603      * @return {Roo.grid.Grid} this
32604      */
32605     render : function()
32606     {
32607         var c = this.container;
32608         // try to detect autoHeight/width mode
32609         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32610             this.autoHeight = true;
32611         }
32612         var view = this.getView();
32613         view.init(this);
32614
32615         c.on("click", this.onClick, this);
32616         c.on("dblclick", this.onDblClick, this);
32617         c.on("contextmenu", this.onContextMenu, this);
32618         c.on("keydown", this.onKeyDown, this);
32619         if (Roo.isTouch) {
32620             c.on("touchstart", this.onTouchStart, this);
32621         }
32622
32623         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32624
32625         this.getSelectionModel().init(this);
32626
32627         view.render();
32628
32629         if(this.loadMask){
32630             this.loadMask = new Roo.LoadMask(this.container,
32631                     Roo.apply({store:this.dataSource}, this.loadMask));
32632         }
32633         
32634         
32635         if (this.toolbar && this.toolbar.xtype) {
32636             this.toolbar.container = this.getView().getHeaderPanel(true);
32637             this.toolbar = new Roo.Toolbar(this.toolbar);
32638         }
32639         if (this.footer && this.footer.xtype) {
32640             this.footer.dataSource = this.getDataSource();
32641             this.footer.container = this.getView().getFooterPanel(true);
32642             this.footer = Roo.factory(this.footer, Roo);
32643         }
32644         if (this.dropTarget && this.dropTarget.xtype) {
32645             delete this.dropTarget.xtype;
32646             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32647         }
32648         
32649         
32650         this.rendered = true;
32651         this.fireEvent('render', this);
32652         return this;
32653     },
32654
32655     /**
32656      * Reconfigures the grid to use a different Store and Column Model.
32657      * The View will be bound to the new objects and refreshed.
32658      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32659      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32660      */
32661     reconfigure : function(dataSource, colModel){
32662         if(this.loadMask){
32663             this.loadMask.destroy();
32664             this.loadMask = new Roo.LoadMask(this.container,
32665                     Roo.apply({store:dataSource}, this.loadMask));
32666         }
32667         this.view.bind(dataSource, colModel);
32668         this.dataSource = dataSource;
32669         this.colModel = colModel;
32670         this.view.refresh(true);
32671     },
32672     /**
32673      * addColumns
32674      * Add's a column, default at the end..
32675      
32676      * @param {int} position to add (default end)
32677      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32678      */
32679     addColumns : function(pos, ar)
32680     {
32681         
32682         for (var i =0;i< ar.length;i++) {
32683             var cfg = ar[i];
32684             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32685             this.cm.lookup[cfg.id] = cfg;
32686         }
32687         
32688         
32689         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32690             pos = this.cm.config.length; //this.cm.config.push(cfg);
32691         } 
32692         pos = Math.max(0,pos);
32693         ar.unshift(0);
32694         ar.unshift(pos);
32695         this.cm.config.splice.apply(this.cm.config, ar);
32696         
32697         
32698         
32699         this.view.generateRules(this.cm);
32700         this.view.refresh(true);
32701         
32702     },
32703     
32704     
32705     
32706     
32707     // private
32708     onKeyDown : function(e){
32709         this.fireEvent("keydown", e);
32710     },
32711
32712     /**
32713      * Destroy this grid.
32714      * @param {Boolean} removeEl True to remove the element
32715      */
32716     destroy : function(removeEl, keepListeners){
32717         if(this.loadMask){
32718             this.loadMask.destroy();
32719         }
32720         var c = this.container;
32721         c.removeAllListeners();
32722         this.view.destroy();
32723         this.colModel.purgeListeners();
32724         if(!keepListeners){
32725             this.purgeListeners();
32726         }
32727         c.update("");
32728         if(removeEl === true){
32729             c.remove();
32730         }
32731     },
32732
32733     // private
32734     processEvent : function(name, e){
32735         // does this fire select???
32736         //Roo.log('grid:processEvent '  + name);
32737         
32738         if (name != 'touchstart' ) {
32739             this.fireEvent(name, e);    
32740         }
32741         
32742         var t = e.getTarget();
32743         var v = this.view;
32744         var header = v.findHeaderIndex(t);
32745         if(header !== false){
32746             var ename = name == 'touchstart' ? 'click' : name;
32747              
32748             this.fireEvent("header" + ename, this, header, e);
32749         }else{
32750             var row = v.findRowIndex(t);
32751             var cell = v.findCellIndex(t);
32752             if (name == 'touchstart') {
32753                 // first touch is always a click.
32754                 // hopefull this happens after selection is updated.?
32755                 name = false;
32756                 
32757                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32758                     var cs = this.selModel.getSelectedCell();
32759                     if (row == cs[0] && cell == cs[1]){
32760                         name = 'dblclick';
32761                     }
32762                 }
32763                 if (typeof(this.selModel.getSelections) != 'undefined') {
32764                     var cs = this.selModel.getSelections();
32765                     var ds = this.dataSource;
32766                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32767                         name = 'dblclick';
32768                     }
32769                 }
32770                 if (!name) {
32771                     return;
32772                 }
32773             }
32774             
32775             
32776             if(row !== false){
32777                 this.fireEvent("row" + name, this, row, e);
32778                 if(cell !== false){
32779                     this.fireEvent("cell" + name, this, row, cell, e);
32780                 }
32781             }
32782         }
32783     },
32784
32785     // private
32786     onClick : function(e){
32787         this.processEvent("click", e);
32788     },
32789    // private
32790     onTouchStart : function(e){
32791         this.processEvent("touchstart", e);
32792     },
32793
32794     // private
32795     onContextMenu : function(e, t){
32796         this.processEvent("contextmenu", e);
32797     },
32798
32799     // private
32800     onDblClick : function(e){
32801         this.processEvent("dblclick", e);
32802     },
32803
32804     // private
32805     walkCells : function(row, col, step, fn, scope){
32806         var cm = this.colModel, clen = cm.getColumnCount();
32807         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32808         if(step < 0){
32809             if(col < 0){
32810                 row--;
32811                 first = false;
32812             }
32813             while(row >= 0){
32814                 if(!first){
32815                     col = clen-1;
32816                 }
32817                 first = false;
32818                 while(col >= 0){
32819                     if(fn.call(scope || this, row, col, cm) === true){
32820                         return [row, col];
32821                     }
32822                     col--;
32823                 }
32824                 row--;
32825             }
32826         } else {
32827             if(col >= clen){
32828                 row++;
32829                 first = false;
32830             }
32831             while(row < rlen){
32832                 if(!first){
32833                     col = 0;
32834                 }
32835                 first = false;
32836                 while(col < clen){
32837                     if(fn.call(scope || this, row, col, cm) === true){
32838                         return [row, col];
32839                     }
32840                     col++;
32841                 }
32842                 row++;
32843             }
32844         }
32845         return null;
32846     },
32847
32848     // private
32849     getSelections : function(){
32850         return this.selModel.getSelections();
32851     },
32852
32853     /**
32854      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32855      * but if manual update is required this method will initiate it.
32856      */
32857     autoSize : function(){
32858         if(this.rendered){
32859             this.view.layout();
32860             if(this.view.adjustForScroll){
32861                 this.view.adjustForScroll();
32862             }
32863         }
32864     },
32865
32866     /**
32867      * Returns the grid's underlying element.
32868      * @return {Element} The element
32869      */
32870     getGridEl : function(){
32871         return this.container;
32872     },
32873
32874     // private for compatibility, overridden by editor grid
32875     stopEditing : function(){},
32876
32877     /**
32878      * Returns the grid's SelectionModel.
32879      * @return {SelectionModel}
32880      */
32881     getSelectionModel : function(){
32882         if(!this.selModel){
32883             this.selModel = new Roo.grid.RowSelectionModel();
32884         }
32885         return this.selModel;
32886     },
32887
32888     /**
32889      * Returns the grid's DataSource.
32890      * @return {DataSource}
32891      */
32892     getDataSource : function(){
32893         return this.dataSource;
32894     },
32895
32896     /**
32897      * Returns the grid's ColumnModel.
32898      * @return {ColumnModel}
32899      */
32900     getColumnModel : function(){
32901         return this.colModel;
32902     },
32903
32904     /**
32905      * Returns the grid's GridView object.
32906      * @return {GridView}
32907      */
32908     getView : function(){
32909         if(!this.view){
32910             this.view = new Roo.grid.GridView(this.viewConfig);
32911             this.relayEvents(this.view, [
32912                 "beforerowremoved", "beforerowsinserted",
32913                 "beforerefresh", "rowremoved",
32914                 "rowsinserted", "rowupdated" ,"refresh"
32915             ]);
32916         }
32917         return this.view;
32918     },
32919     /**
32920      * Called to get grid's drag proxy text, by default returns this.ddText.
32921      * Override this to put something different in the dragged text.
32922      * @return {String}
32923      */
32924     getDragDropText : function(){
32925         var count = this.selModel.getCount();
32926         return String.format(this.ddText, count, count == 1 ? '' : 's');
32927     }
32928 });
32929 /*
32930  * Based on:
32931  * Ext JS Library 1.1.1
32932  * Copyright(c) 2006-2007, Ext JS, LLC.
32933  *
32934  * Originally Released Under LGPL - original licence link has changed is not relivant.
32935  *
32936  * Fork - LGPL
32937  * <script type="text/javascript">
32938  */
32939  
32940 Roo.grid.AbstractGridView = function(){
32941         this.grid = null;
32942         
32943         this.events = {
32944             "beforerowremoved" : true,
32945             "beforerowsinserted" : true,
32946             "beforerefresh" : true,
32947             "rowremoved" : true,
32948             "rowsinserted" : true,
32949             "rowupdated" : true,
32950             "refresh" : true
32951         };
32952     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32953 };
32954
32955 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32956     rowClass : "x-grid-row",
32957     cellClass : "x-grid-cell",
32958     tdClass : "x-grid-td",
32959     hdClass : "x-grid-hd",
32960     splitClass : "x-grid-hd-split",
32961     
32962     init: function(grid){
32963         this.grid = grid;
32964                 var cid = this.grid.getGridEl().id;
32965         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32966         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32967         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32968         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32969         },
32970         
32971     getColumnRenderers : function(){
32972         var renderers = [];
32973         var cm = this.grid.colModel;
32974         var colCount = cm.getColumnCount();
32975         for(var i = 0; i < colCount; i++){
32976             renderers[i] = cm.getRenderer(i);
32977         }
32978         return renderers;
32979     },
32980     
32981     getColumnIds : function(){
32982         var ids = [];
32983         var cm = this.grid.colModel;
32984         var colCount = cm.getColumnCount();
32985         for(var i = 0; i < colCount; i++){
32986             ids[i] = cm.getColumnId(i);
32987         }
32988         return ids;
32989     },
32990     
32991     getDataIndexes : function(){
32992         if(!this.indexMap){
32993             this.indexMap = this.buildIndexMap();
32994         }
32995         return this.indexMap.colToData;
32996     },
32997     
32998     getColumnIndexByDataIndex : function(dataIndex){
32999         if(!this.indexMap){
33000             this.indexMap = this.buildIndexMap();
33001         }
33002         return this.indexMap.dataToCol[dataIndex];
33003     },
33004     
33005     /**
33006      * Set a css style for a column dynamically. 
33007      * @param {Number} colIndex The index of the column
33008      * @param {String} name The css property name
33009      * @param {String} value The css value
33010      */
33011     setCSSStyle : function(colIndex, name, value){
33012         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33013         Roo.util.CSS.updateRule(selector, name, value);
33014     },
33015     
33016     generateRules : function(cm){
33017         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33018         Roo.util.CSS.removeStyleSheet(rulesId);
33019         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33020             var cid = cm.getColumnId(i);
33021             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33022                          this.tdSelector, cid, " {\n}\n",
33023                          this.hdSelector, cid, " {\n}\n",
33024                          this.splitSelector, cid, " {\n}\n");
33025         }
33026         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33027     }
33028 });/*
33029  * Based on:
33030  * Ext JS Library 1.1.1
33031  * Copyright(c) 2006-2007, Ext JS, LLC.
33032  *
33033  * Originally Released Under LGPL - original licence link has changed is not relivant.
33034  *
33035  * Fork - LGPL
33036  * <script type="text/javascript">
33037  */
33038
33039 // private
33040 // This is a support class used internally by the Grid components
33041 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33042     this.grid = grid;
33043     this.view = grid.getView();
33044     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33045     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33046     if(hd2){
33047         this.setHandleElId(Roo.id(hd));
33048         this.setOuterHandleElId(Roo.id(hd2));
33049     }
33050     this.scroll = false;
33051 };
33052 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33053     maxDragWidth: 120,
33054     getDragData : function(e){
33055         var t = Roo.lib.Event.getTarget(e);
33056         var h = this.view.findHeaderCell(t);
33057         if(h){
33058             return {ddel: h.firstChild, header:h};
33059         }
33060         return false;
33061     },
33062
33063     onInitDrag : function(e){
33064         this.view.headersDisabled = true;
33065         var clone = this.dragData.ddel.cloneNode(true);
33066         clone.id = Roo.id();
33067         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33068         this.proxy.update(clone);
33069         return true;
33070     },
33071
33072     afterValidDrop : function(){
33073         var v = this.view;
33074         setTimeout(function(){
33075             v.headersDisabled = false;
33076         }, 50);
33077     },
33078
33079     afterInvalidDrop : function(){
33080         var v = this.view;
33081         setTimeout(function(){
33082             v.headersDisabled = false;
33083         }, 50);
33084     }
33085 });
33086 /*
33087  * Based on:
33088  * Ext JS Library 1.1.1
33089  * Copyright(c) 2006-2007, Ext JS, LLC.
33090  *
33091  * Originally Released Under LGPL - original licence link has changed is not relivant.
33092  *
33093  * Fork - LGPL
33094  * <script type="text/javascript">
33095  */
33096 // private
33097 // This is a support class used internally by the Grid components
33098 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33099     this.grid = grid;
33100     this.view = grid.getView();
33101     // split the proxies so they don't interfere with mouse events
33102     this.proxyTop = Roo.DomHelper.append(document.body, {
33103         cls:"col-move-top", html:"&#160;"
33104     }, true);
33105     this.proxyBottom = Roo.DomHelper.append(document.body, {
33106         cls:"col-move-bottom", html:"&#160;"
33107     }, true);
33108     this.proxyTop.hide = this.proxyBottom.hide = function(){
33109         this.setLeftTop(-100,-100);
33110         this.setStyle("visibility", "hidden");
33111     };
33112     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33113     // temporarily disabled
33114     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33115     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33116 };
33117 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33118     proxyOffsets : [-4, -9],
33119     fly: Roo.Element.fly,
33120
33121     getTargetFromEvent : function(e){
33122         var t = Roo.lib.Event.getTarget(e);
33123         var cindex = this.view.findCellIndex(t);
33124         if(cindex !== false){
33125             return this.view.getHeaderCell(cindex);
33126         }
33127         return null;
33128     },
33129
33130     nextVisible : function(h){
33131         var v = this.view, cm = this.grid.colModel;
33132         h = h.nextSibling;
33133         while(h){
33134             if(!cm.isHidden(v.getCellIndex(h))){
33135                 return h;
33136             }
33137             h = h.nextSibling;
33138         }
33139         return null;
33140     },
33141
33142     prevVisible : function(h){
33143         var v = this.view, cm = this.grid.colModel;
33144         h = h.prevSibling;
33145         while(h){
33146             if(!cm.isHidden(v.getCellIndex(h))){
33147                 return h;
33148             }
33149             h = h.prevSibling;
33150         }
33151         return null;
33152     },
33153
33154     positionIndicator : function(h, n, e){
33155         var x = Roo.lib.Event.getPageX(e);
33156         var r = Roo.lib.Dom.getRegion(n.firstChild);
33157         var px, pt, py = r.top + this.proxyOffsets[1];
33158         if((r.right - x) <= (r.right-r.left)/2){
33159             px = r.right+this.view.borderWidth;
33160             pt = "after";
33161         }else{
33162             px = r.left;
33163             pt = "before";
33164         }
33165         var oldIndex = this.view.getCellIndex(h);
33166         var newIndex = this.view.getCellIndex(n);
33167
33168         if(this.grid.colModel.isFixed(newIndex)){
33169             return false;
33170         }
33171
33172         var locked = this.grid.colModel.isLocked(newIndex);
33173
33174         if(pt == "after"){
33175             newIndex++;
33176         }
33177         if(oldIndex < newIndex){
33178             newIndex--;
33179         }
33180         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33181             return false;
33182         }
33183         px +=  this.proxyOffsets[0];
33184         this.proxyTop.setLeftTop(px, py);
33185         this.proxyTop.show();
33186         if(!this.bottomOffset){
33187             this.bottomOffset = this.view.mainHd.getHeight();
33188         }
33189         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33190         this.proxyBottom.show();
33191         return pt;
33192     },
33193
33194     onNodeEnter : function(n, dd, e, data){
33195         if(data.header != n){
33196             this.positionIndicator(data.header, n, e);
33197         }
33198     },
33199
33200     onNodeOver : function(n, dd, e, data){
33201         var result = false;
33202         if(data.header != n){
33203             result = this.positionIndicator(data.header, n, e);
33204         }
33205         if(!result){
33206             this.proxyTop.hide();
33207             this.proxyBottom.hide();
33208         }
33209         return result ? this.dropAllowed : this.dropNotAllowed;
33210     },
33211
33212     onNodeOut : function(n, dd, e, data){
33213         this.proxyTop.hide();
33214         this.proxyBottom.hide();
33215     },
33216
33217     onNodeDrop : function(n, dd, e, data){
33218         var h = data.header;
33219         if(h != n){
33220             var cm = this.grid.colModel;
33221             var x = Roo.lib.Event.getPageX(e);
33222             var r = Roo.lib.Dom.getRegion(n.firstChild);
33223             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33224             var oldIndex = this.view.getCellIndex(h);
33225             var newIndex = this.view.getCellIndex(n);
33226             var locked = cm.isLocked(newIndex);
33227             if(pt == "after"){
33228                 newIndex++;
33229             }
33230             if(oldIndex < newIndex){
33231                 newIndex--;
33232             }
33233             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33234                 return false;
33235             }
33236             cm.setLocked(oldIndex, locked, true);
33237             cm.moveColumn(oldIndex, newIndex);
33238             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33239             return true;
33240         }
33241         return false;
33242     }
33243 });
33244 /*
33245  * Based on:
33246  * Ext JS Library 1.1.1
33247  * Copyright(c) 2006-2007, Ext JS, LLC.
33248  *
33249  * Originally Released Under LGPL - original licence link has changed is not relivant.
33250  *
33251  * Fork - LGPL
33252  * <script type="text/javascript">
33253  */
33254   
33255 /**
33256  * @class Roo.grid.GridView
33257  * @extends Roo.util.Observable
33258  *
33259  * @constructor
33260  * @param {Object} config
33261  */
33262 Roo.grid.GridView = function(config){
33263     Roo.grid.GridView.superclass.constructor.call(this);
33264     this.el = null;
33265
33266     Roo.apply(this, config);
33267 };
33268
33269 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33270
33271     unselectable :  'unselectable="on"',
33272     unselectableCls :  'x-unselectable',
33273     
33274     
33275     rowClass : "x-grid-row",
33276
33277     cellClass : "x-grid-col",
33278
33279     tdClass : "x-grid-td",
33280
33281     hdClass : "x-grid-hd",
33282
33283     splitClass : "x-grid-split",
33284
33285     sortClasses : ["sort-asc", "sort-desc"],
33286
33287     enableMoveAnim : false,
33288
33289     hlColor: "C3DAF9",
33290
33291     dh : Roo.DomHelper,
33292
33293     fly : Roo.Element.fly,
33294
33295     css : Roo.util.CSS,
33296
33297     borderWidth: 1,
33298
33299     splitOffset: 3,
33300
33301     scrollIncrement : 22,
33302
33303     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33304
33305     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33306
33307     bind : function(ds, cm){
33308         if(this.ds){
33309             this.ds.un("load", this.onLoad, this);
33310             this.ds.un("datachanged", this.onDataChange, this);
33311             this.ds.un("add", this.onAdd, this);
33312             this.ds.un("remove", this.onRemove, this);
33313             this.ds.un("update", this.onUpdate, this);
33314             this.ds.un("clear", this.onClear, this);
33315         }
33316         if(ds){
33317             ds.on("load", this.onLoad, this);
33318             ds.on("datachanged", this.onDataChange, this);
33319             ds.on("add", this.onAdd, this);
33320             ds.on("remove", this.onRemove, this);
33321             ds.on("update", this.onUpdate, this);
33322             ds.on("clear", this.onClear, this);
33323         }
33324         this.ds = ds;
33325
33326         if(this.cm){
33327             this.cm.un("widthchange", this.onColWidthChange, this);
33328             this.cm.un("headerchange", this.onHeaderChange, this);
33329             this.cm.un("hiddenchange", this.onHiddenChange, this);
33330             this.cm.un("columnmoved", this.onColumnMove, this);
33331             this.cm.un("columnlockchange", this.onColumnLock, this);
33332         }
33333         if(cm){
33334             this.generateRules(cm);
33335             cm.on("widthchange", this.onColWidthChange, this);
33336             cm.on("headerchange", this.onHeaderChange, this);
33337             cm.on("hiddenchange", this.onHiddenChange, this);
33338             cm.on("columnmoved", this.onColumnMove, this);
33339             cm.on("columnlockchange", this.onColumnLock, this);
33340         }
33341         this.cm = cm;
33342     },
33343
33344     init: function(grid){
33345         Roo.grid.GridView.superclass.init.call(this, grid);
33346
33347         this.bind(grid.dataSource, grid.colModel);
33348
33349         grid.on("headerclick", this.handleHeaderClick, this);
33350
33351         if(grid.trackMouseOver){
33352             grid.on("mouseover", this.onRowOver, this);
33353             grid.on("mouseout", this.onRowOut, this);
33354         }
33355         grid.cancelTextSelection = function(){};
33356         this.gridId = grid.id;
33357
33358         var tpls = this.templates || {};
33359
33360         if(!tpls.master){
33361             tpls.master = new Roo.Template(
33362                '<div class="x-grid" hidefocus="true">',
33363                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33364                   '<div class="x-grid-topbar"></div>',
33365                   '<div class="x-grid-scroller"><div></div></div>',
33366                   '<div class="x-grid-locked">',
33367                       '<div class="x-grid-header">{lockedHeader}</div>',
33368                       '<div class="x-grid-body">{lockedBody}</div>',
33369                   "</div>",
33370                   '<div class="x-grid-viewport">',
33371                       '<div class="x-grid-header">{header}</div>',
33372                       '<div class="x-grid-body">{body}</div>',
33373                   "</div>",
33374                   '<div class="x-grid-bottombar"></div>',
33375                  
33376                   '<div class="x-grid-resize-proxy">&#160;</div>',
33377                "</div>"
33378             );
33379             tpls.master.disableformats = true;
33380         }
33381
33382         if(!tpls.header){
33383             tpls.header = new Roo.Template(
33384                '<table border="0" cellspacing="0" cellpadding="0">',
33385                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33386                "</table>{splits}"
33387             );
33388             tpls.header.disableformats = true;
33389         }
33390         tpls.header.compile();
33391
33392         if(!tpls.hcell){
33393             tpls.hcell = new Roo.Template(
33394                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33395                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33396                 "</div></td>"
33397              );
33398              tpls.hcell.disableFormats = true;
33399         }
33400         tpls.hcell.compile();
33401
33402         if(!tpls.hsplit){
33403             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33404                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33405             tpls.hsplit.disableFormats = true;
33406         }
33407         tpls.hsplit.compile();
33408
33409         if(!tpls.body){
33410             tpls.body = new Roo.Template(
33411                '<table border="0" cellspacing="0" cellpadding="0">',
33412                "<tbody>{rows}</tbody>",
33413                "</table>"
33414             );
33415             tpls.body.disableFormats = true;
33416         }
33417         tpls.body.compile();
33418
33419         if(!tpls.row){
33420             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33421             tpls.row.disableFormats = true;
33422         }
33423         tpls.row.compile();
33424
33425         if(!tpls.cell){
33426             tpls.cell = new Roo.Template(
33427                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33428                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33429                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33430                 "</td>"
33431             );
33432             tpls.cell.disableFormats = true;
33433         }
33434         tpls.cell.compile();
33435
33436         this.templates = tpls;
33437     },
33438
33439     // remap these for backwards compat
33440     onColWidthChange : function(){
33441         this.updateColumns.apply(this, arguments);
33442     },
33443     onHeaderChange : function(){
33444         this.updateHeaders.apply(this, arguments);
33445     }, 
33446     onHiddenChange : function(){
33447         this.handleHiddenChange.apply(this, arguments);
33448     },
33449     onColumnMove : function(){
33450         this.handleColumnMove.apply(this, arguments);
33451     },
33452     onColumnLock : function(){
33453         this.handleLockChange.apply(this, arguments);
33454     },
33455
33456     onDataChange : function(){
33457         this.refresh();
33458         this.updateHeaderSortState();
33459     },
33460
33461     onClear : function(){
33462         this.refresh();
33463     },
33464
33465     onUpdate : function(ds, record){
33466         this.refreshRow(record);
33467     },
33468
33469     refreshRow : function(record){
33470         var ds = this.ds, index;
33471         if(typeof record == 'number'){
33472             index = record;
33473             record = ds.getAt(index);
33474         }else{
33475             index = ds.indexOf(record);
33476         }
33477         this.insertRows(ds, index, index, true);
33478         this.onRemove(ds, record, index+1, true);
33479         this.syncRowHeights(index, index);
33480         this.layout();
33481         this.fireEvent("rowupdated", this, index, record);
33482     },
33483
33484     onAdd : function(ds, records, index){
33485         this.insertRows(ds, index, index + (records.length-1));
33486     },
33487
33488     onRemove : function(ds, record, index, isUpdate){
33489         if(isUpdate !== true){
33490             this.fireEvent("beforerowremoved", this, index, record);
33491         }
33492         var bt = this.getBodyTable(), lt = this.getLockedTable();
33493         if(bt.rows[index]){
33494             bt.firstChild.removeChild(bt.rows[index]);
33495         }
33496         if(lt.rows[index]){
33497             lt.firstChild.removeChild(lt.rows[index]);
33498         }
33499         if(isUpdate !== true){
33500             this.stripeRows(index);
33501             this.syncRowHeights(index, index);
33502             this.layout();
33503             this.fireEvent("rowremoved", this, index, record);
33504         }
33505     },
33506
33507     onLoad : function(){
33508         this.scrollToTop();
33509     },
33510
33511     /**
33512      * Scrolls the grid to the top
33513      */
33514     scrollToTop : function(){
33515         if(this.scroller){
33516             this.scroller.dom.scrollTop = 0;
33517             this.syncScroll();
33518         }
33519     },
33520
33521     /**
33522      * Gets a panel in the header of the grid that can be used for toolbars etc.
33523      * After modifying the contents of this panel a call to grid.autoSize() may be
33524      * required to register any changes in size.
33525      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33526      * @return Roo.Element
33527      */
33528     getHeaderPanel : function(doShow){
33529         if(doShow){
33530             this.headerPanel.show();
33531         }
33532         return this.headerPanel;
33533     },
33534
33535     /**
33536      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33537      * After modifying the contents of this panel a call to grid.autoSize() may be
33538      * required to register any changes in size.
33539      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33540      * @return Roo.Element
33541      */
33542     getFooterPanel : function(doShow){
33543         if(doShow){
33544             this.footerPanel.show();
33545         }
33546         return this.footerPanel;
33547     },
33548
33549     initElements : function(){
33550         var E = Roo.Element;
33551         var el = this.grid.getGridEl().dom.firstChild;
33552         var cs = el.childNodes;
33553
33554         this.el = new E(el);
33555         
33556          this.focusEl = new E(el.firstChild);
33557         this.focusEl.swallowEvent("click", true);
33558         
33559         this.headerPanel = new E(cs[1]);
33560         this.headerPanel.enableDisplayMode("block");
33561
33562         this.scroller = new E(cs[2]);
33563         this.scrollSizer = new E(this.scroller.dom.firstChild);
33564
33565         this.lockedWrap = new E(cs[3]);
33566         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33567         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33568
33569         this.mainWrap = new E(cs[4]);
33570         this.mainHd = new E(this.mainWrap.dom.firstChild);
33571         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33572
33573         this.footerPanel = new E(cs[5]);
33574         this.footerPanel.enableDisplayMode("block");
33575
33576         this.resizeProxy = new E(cs[6]);
33577
33578         this.headerSelector = String.format(
33579            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33580            this.lockedHd.id, this.mainHd.id
33581         );
33582
33583         this.splitterSelector = String.format(
33584            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33585            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33586         );
33587     },
33588     idToCssName : function(s)
33589     {
33590         return s.replace(/[^a-z0-9]+/ig, '-');
33591     },
33592
33593     getHeaderCell : function(index){
33594         return Roo.DomQuery.select(this.headerSelector)[index];
33595     },
33596
33597     getHeaderCellMeasure : function(index){
33598         return this.getHeaderCell(index).firstChild;
33599     },
33600
33601     getHeaderCellText : function(index){
33602         return this.getHeaderCell(index).firstChild.firstChild;
33603     },
33604
33605     getLockedTable : function(){
33606         return this.lockedBody.dom.firstChild;
33607     },
33608
33609     getBodyTable : function(){
33610         return this.mainBody.dom.firstChild;
33611     },
33612
33613     getLockedRow : function(index){
33614         return this.getLockedTable().rows[index];
33615     },
33616
33617     getRow : function(index){
33618         return this.getBodyTable().rows[index];
33619     },
33620
33621     getRowComposite : function(index){
33622         if(!this.rowEl){
33623             this.rowEl = new Roo.CompositeElementLite();
33624         }
33625         var els = [], lrow, mrow;
33626         if(lrow = this.getLockedRow(index)){
33627             els.push(lrow);
33628         }
33629         if(mrow = this.getRow(index)){
33630             els.push(mrow);
33631         }
33632         this.rowEl.elements = els;
33633         return this.rowEl;
33634     },
33635     /**
33636      * Gets the 'td' of the cell
33637      * 
33638      * @param {Integer} rowIndex row to select
33639      * @param {Integer} colIndex column to select
33640      * 
33641      * @return {Object} 
33642      */
33643     getCell : function(rowIndex, colIndex){
33644         var locked = this.cm.getLockedCount();
33645         var source;
33646         if(colIndex < locked){
33647             source = this.lockedBody.dom.firstChild;
33648         }else{
33649             source = this.mainBody.dom.firstChild;
33650             colIndex -= locked;
33651         }
33652         return source.rows[rowIndex].childNodes[colIndex];
33653     },
33654
33655     getCellText : function(rowIndex, colIndex){
33656         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33657     },
33658
33659     getCellBox : function(cell){
33660         var b = this.fly(cell).getBox();
33661         if(Roo.isOpera){ // opera fails to report the Y
33662             b.y = cell.offsetTop + this.mainBody.getY();
33663         }
33664         return b;
33665     },
33666
33667     getCellIndex : function(cell){
33668         var id = String(cell.className).match(this.cellRE);
33669         if(id){
33670             return parseInt(id[1], 10);
33671         }
33672         return 0;
33673     },
33674
33675     findHeaderIndex : function(n){
33676         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33677         return r ? this.getCellIndex(r) : false;
33678     },
33679
33680     findHeaderCell : function(n){
33681         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33682         return r ? r : false;
33683     },
33684
33685     findRowIndex : function(n){
33686         if(!n){
33687             return false;
33688         }
33689         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33690         return r ? r.rowIndex : false;
33691     },
33692
33693     findCellIndex : function(node){
33694         var stop = this.el.dom;
33695         while(node && node != stop){
33696             if(this.findRE.test(node.className)){
33697                 return this.getCellIndex(node);
33698             }
33699             node = node.parentNode;
33700         }
33701         return false;
33702     },
33703
33704     getColumnId : function(index){
33705         return this.cm.getColumnId(index);
33706     },
33707
33708     getSplitters : function()
33709     {
33710         if(this.splitterSelector){
33711            return Roo.DomQuery.select(this.splitterSelector);
33712         }else{
33713             return null;
33714       }
33715     },
33716
33717     getSplitter : function(index){
33718         return this.getSplitters()[index];
33719     },
33720
33721     onRowOver : function(e, t){
33722         var row;
33723         if((row = this.findRowIndex(t)) !== false){
33724             this.getRowComposite(row).addClass("x-grid-row-over");
33725         }
33726     },
33727
33728     onRowOut : function(e, t){
33729         var row;
33730         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33731             this.getRowComposite(row).removeClass("x-grid-row-over");
33732         }
33733     },
33734
33735     renderHeaders : function(){
33736         var cm = this.cm;
33737         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33738         var cb = [], lb = [], sb = [], lsb = [], p = {};
33739         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33740             p.cellId = "x-grid-hd-0-" + i;
33741             p.splitId = "x-grid-csplit-0-" + i;
33742             p.id = cm.getColumnId(i);
33743             p.value = cm.getColumnHeader(i) || "";
33744             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33745             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33746             if(!cm.isLocked(i)){
33747                 cb[cb.length] = ct.apply(p);
33748                 sb[sb.length] = st.apply(p);
33749             }else{
33750                 lb[lb.length] = ct.apply(p);
33751                 lsb[lsb.length] = st.apply(p);
33752             }
33753         }
33754         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33755                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33756     },
33757
33758     updateHeaders : function(){
33759         var html = this.renderHeaders();
33760         this.lockedHd.update(html[0]);
33761         this.mainHd.update(html[1]);
33762     },
33763
33764     /**
33765      * Focuses the specified row.
33766      * @param {Number} row The row index
33767      */
33768     focusRow : function(row)
33769     {
33770         //Roo.log('GridView.focusRow');
33771         var x = this.scroller.dom.scrollLeft;
33772         this.focusCell(row, 0, false);
33773         this.scroller.dom.scrollLeft = x;
33774     },
33775
33776     /**
33777      * Focuses the specified cell.
33778      * @param {Number} row The row index
33779      * @param {Number} col The column index
33780      * @param {Boolean} hscroll false to disable horizontal scrolling
33781      */
33782     focusCell : function(row, col, hscroll)
33783     {
33784         //Roo.log('GridView.focusCell');
33785         var el = this.ensureVisible(row, col, hscroll);
33786         this.focusEl.alignTo(el, "tl-tl");
33787         if(Roo.isGecko){
33788             this.focusEl.focus();
33789         }else{
33790             this.focusEl.focus.defer(1, this.focusEl);
33791         }
33792     },
33793
33794     /**
33795      * Scrolls the specified cell into view
33796      * @param {Number} row The row index
33797      * @param {Number} col The column index
33798      * @param {Boolean} hscroll false to disable horizontal scrolling
33799      */
33800     ensureVisible : function(row, col, hscroll)
33801     {
33802         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33803         //return null; //disable for testing.
33804         if(typeof row != "number"){
33805             row = row.rowIndex;
33806         }
33807         if(row < 0 && row >= this.ds.getCount()){
33808             return  null;
33809         }
33810         col = (col !== undefined ? col : 0);
33811         var cm = this.grid.colModel;
33812         while(cm.isHidden(col)){
33813             col++;
33814         }
33815
33816         var el = this.getCell(row, col);
33817         if(!el){
33818             return null;
33819         }
33820         var c = this.scroller.dom;
33821
33822         var ctop = parseInt(el.offsetTop, 10);
33823         var cleft = parseInt(el.offsetLeft, 10);
33824         var cbot = ctop + el.offsetHeight;
33825         var cright = cleft + el.offsetWidth;
33826         
33827         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33828         var stop = parseInt(c.scrollTop, 10);
33829         var sleft = parseInt(c.scrollLeft, 10);
33830         var sbot = stop + ch;
33831         var sright = sleft + c.clientWidth;
33832         /*
33833         Roo.log('GridView.ensureVisible:' +
33834                 ' ctop:' + ctop +
33835                 ' c.clientHeight:' + c.clientHeight +
33836                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33837                 ' stop:' + stop +
33838                 ' cbot:' + cbot +
33839                 ' sbot:' + sbot +
33840                 ' ch:' + ch  
33841                 );
33842         */
33843         if(ctop < stop){
33844             c.scrollTop = ctop;
33845             //Roo.log("set scrolltop to ctop DISABLE?");
33846         }else if(cbot > sbot){
33847             //Roo.log("set scrolltop to cbot-ch");
33848             c.scrollTop = cbot-ch;
33849         }
33850         
33851         if(hscroll !== false){
33852             if(cleft < sleft){
33853                 c.scrollLeft = cleft;
33854             }else if(cright > sright){
33855                 c.scrollLeft = cright-c.clientWidth;
33856             }
33857         }
33858          
33859         return el;
33860     },
33861
33862     updateColumns : function(){
33863         this.grid.stopEditing();
33864         var cm = this.grid.colModel, colIds = this.getColumnIds();
33865         //var totalWidth = cm.getTotalWidth();
33866         var pos = 0;
33867         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33868             //if(cm.isHidden(i)) continue;
33869             var w = cm.getColumnWidth(i);
33870             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33871             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33872         }
33873         this.updateSplitters();
33874     },
33875
33876     generateRules : function(cm){
33877         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33878         Roo.util.CSS.removeStyleSheet(rulesId);
33879         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33880             var cid = cm.getColumnId(i);
33881             var align = '';
33882             if(cm.config[i].align){
33883                 align = 'text-align:'+cm.config[i].align+';';
33884             }
33885             var hidden = '';
33886             if(cm.isHidden(i)){
33887                 hidden = 'display:none;';
33888             }
33889             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33890             ruleBuf.push(
33891                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33892                     this.hdSelector, cid, " {\n", align, width, "}\n",
33893                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33894                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33895         }
33896         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33897     },
33898
33899     updateSplitters : function(){
33900         var cm = this.cm, s = this.getSplitters();
33901         if(s){ // splitters not created yet
33902             var pos = 0, locked = true;
33903             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33904                 if(cm.isHidden(i)) {
33905                     continue;
33906                 }
33907                 var w = cm.getColumnWidth(i); // make sure it's a number
33908                 if(!cm.isLocked(i) && locked){
33909                     pos = 0;
33910                     locked = false;
33911                 }
33912                 pos += w;
33913                 s[i].style.left = (pos-this.splitOffset) + "px";
33914             }
33915         }
33916     },
33917
33918     handleHiddenChange : function(colModel, colIndex, hidden){
33919         if(hidden){
33920             this.hideColumn(colIndex);
33921         }else{
33922             this.unhideColumn(colIndex);
33923         }
33924     },
33925
33926     hideColumn : function(colIndex){
33927         var cid = this.getColumnId(colIndex);
33928         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33929         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33930         if(Roo.isSafari){
33931             this.updateHeaders();
33932         }
33933         this.updateSplitters();
33934         this.layout();
33935     },
33936
33937     unhideColumn : function(colIndex){
33938         var cid = this.getColumnId(colIndex);
33939         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33940         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33941
33942         if(Roo.isSafari){
33943             this.updateHeaders();
33944         }
33945         this.updateSplitters();
33946         this.layout();
33947     },
33948
33949     insertRows : function(dm, firstRow, lastRow, isUpdate){
33950         if(firstRow == 0 && lastRow == dm.getCount()-1){
33951             this.refresh();
33952         }else{
33953             if(!isUpdate){
33954                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33955             }
33956             var s = this.getScrollState();
33957             var markup = this.renderRows(firstRow, lastRow);
33958             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33959             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33960             this.restoreScroll(s);
33961             if(!isUpdate){
33962                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33963                 this.syncRowHeights(firstRow, lastRow);
33964                 this.stripeRows(firstRow);
33965                 this.layout();
33966             }
33967         }
33968     },
33969
33970     bufferRows : function(markup, target, index){
33971         var before = null, trows = target.rows, tbody = target.tBodies[0];
33972         if(index < trows.length){
33973             before = trows[index];
33974         }
33975         var b = document.createElement("div");
33976         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33977         var rows = b.firstChild.rows;
33978         for(var i = 0, len = rows.length; i < len; i++){
33979             if(before){
33980                 tbody.insertBefore(rows[0], before);
33981             }else{
33982                 tbody.appendChild(rows[0]);
33983             }
33984         }
33985         b.innerHTML = "";
33986         b = null;
33987     },
33988
33989     deleteRows : function(dm, firstRow, lastRow){
33990         if(dm.getRowCount()<1){
33991             this.fireEvent("beforerefresh", this);
33992             this.mainBody.update("");
33993             this.lockedBody.update("");
33994             this.fireEvent("refresh", this);
33995         }else{
33996             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33997             var bt = this.getBodyTable();
33998             var tbody = bt.firstChild;
33999             var rows = bt.rows;
34000             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34001                 tbody.removeChild(rows[firstRow]);
34002             }
34003             this.stripeRows(firstRow);
34004             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34005         }
34006     },
34007
34008     updateRows : function(dataSource, firstRow, lastRow){
34009         var s = this.getScrollState();
34010         this.refresh();
34011         this.restoreScroll(s);
34012     },
34013
34014     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34015         if(!noRefresh){
34016            this.refresh();
34017         }
34018         this.updateHeaderSortState();
34019     },
34020
34021     getScrollState : function(){
34022         
34023         var sb = this.scroller.dom;
34024         return {left: sb.scrollLeft, top: sb.scrollTop};
34025     },
34026
34027     stripeRows : function(startRow){
34028         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34029             return;
34030         }
34031         startRow = startRow || 0;
34032         var rows = this.getBodyTable().rows;
34033         var lrows = this.getLockedTable().rows;
34034         var cls = ' x-grid-row-alt ';
34035         for(var i = startRow, len = rows.length; i < len; i++){
34036             var row = rows[i], lrow = lrows[i];
34037             var isAlt = ((i+1) % 2 == 0);
34038             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34039             if(isAlt == hasAlt){
34040                 continue;
34041             }
34042             if(isAlt){
34043                 row.className += " x-grid-row-alt";
34044             }else{
34045                 row.className = row.className.replace("x-grid-row-alt", "");
34046             }
34047             if(lrow){
34048                 lrow.className = row.className;
34049             }
34050         }
34051     },
34052
34053     restoreScroll : function(state){
34054         //Roo.log('GridView.restoreScroll');
34055         var sb = this.scroller.dom;
34056         sb.scrollLeft = state.left;
34057         sb.scrollTop = state.top;
34058         this.syncScroll();
34059     },
34060
34061     syncScroll : function(){
34062         //Roo.log('GridView.syncScroll');
34063         var sb = this.scroller.dom;
34064         var sh = this.mainHd.dom;
34065         var bs = this.mainBody.dom;
34066         var lv = this.lockedBody.dom;
34067         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34068         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34069     },
34070
34071     handleScroll : function(e){
34072         this.syncScroll();
34073         var sb = this.scroller.dom;
34074         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34075         e.stopEvent();
34076     },
34077
34078     handleWheel : function(e){
34079         var d = e.getWheelDelta();
34080         this.scroller.dom.scrollTop -= d*22;
34081         // set this here to prevent jumpy scrolling on large tables
34082         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34083         e.stopEvent();
34084     },
34085
34086     renderRows : function(startRow, endRow){
34087         // pull in all the crap needed to render rows
34088         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34089         var colCount = cm.getColumnCount();
34090
34091         if(ds.getCount() < 1){
34092             return ["", ""];
34093         }
34094
34095         // build a map for all the columns
34096         var cs = [];
34097         for(var i = 0; i < colCount; i++){
34098             var name = cm.getDataIndex(i);
34099             cs[i] = {
34100                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34101                 renderer : cm.getRenderer(i),
34102                 id : cm.getColumnId(i),
34103                 locked : cm.isLocked(i),
34104                 has_editor : cm.isCellEditable(i)
34105             };
34106         }
34107
34108         startRow = startRow || 0;
34109         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34110
34111         // records to render
34112         var rs = ds.getRange(startRow, endRow);
34113
34114         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34115     },
34116
34117     // As much as I hate to duplicate code, this was branched because FireFox really hates
34118     // [].join("") on strings. The performance difference was substantial enough to
34119     // branch this function
34120     doRender : Roo.isGecko ?
34121             function(cs, rs, ds, startRow, colCount, stripe){
34122                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34123                 // buffers
34124                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34125                 
34126                 var hasListener = this.grid.hasListener('rowclass');
34127                 var rowcfg = {};
34128                 for(var j = 0, len = rs.length; j < len; j++){
34129                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34130                     for(var i = 0; i < colCount; i++){
34131                         c = cs[i];
34132                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34133                         p.id = c.id;
34134                         p.css = p.attr = "";
34135                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34136                         if(p.value == undefined || p.value === "") {
34137                             p.value = "&#160;";
34138                         }
34139                         if(c.has_editor){
34140                             p.css += ' x-grid-editable-cell';
34141                         }
34142                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34143                             p.css +=  ' x-grid-dirty-cell';
34144                         }
34145                         var markup = ct.apply(p);
34146                         if(!c.locked){
34147                             cb+= markup;
34148                         }else{
34149                             lcb+= markup;
34150                         }
34151                     }
34152                     var alt = [];
34153                     if(stripe && ((rowIndex+1) % 2 == 0)){
34154                         alt.push("x-grid-row-alt")
34155                     }
34156                     if(r.dirty){
34157                         alt.push(  " x-grid-dirty-row");
34158                     }
34159                     rp.cells = lcb;
34160                     if(this.getRowClass){
34161                         alt.push(this.getRowClass(r, rowIndex));
34162                     }
34163                     if (hasListener) {
34164                         rowcfg = {
34165                              
34166                             record: r,
34167                             rowIndex : rowIndex,
34168                             rowClass : ''
34169                         };
34170                         this.grid.fireEvent('rowclass', this, rowcfg);
34171                         alt.push(rowcfg.rowClass);
34172                     }
34173                     rp.alt = alt.join(" ");
34174                     lbuf+= rt.apply(rp);
34175                     rp.cells = cb;
34176                     buf+=  rt.apply(rp);
34177                 }
34178                 return [lbuf, buf];
34179             } :
34180             function(cs, rs, ds, startRow, colCount, stripe){
34181                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34182                 // buffers
34183                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34184                 var hasListener = this.grid.hasListener('rowclass');
34185  
34186                 var rowcfg = {};
34187                 for(var j = 0, len = rs.length; j < len; j++){
34188                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34189                     for(var i = 0; i < colCount; i++){
34190                         c = cs[i];
34191                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34192                         p.id = c.id;
34193                         p.css = p.attr = "";
34194                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34195                         if(p.value == undefined || p.value === "") {
34196                             p.value = "&#160;";
34197                         }
34198                         //Roo.log(c);
34199                          if(c.has_editor){
34200                             p.css += ' x-grid-editable-cell';
34201                         }
34202                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34203                             p.css += ' x-grid-dirty-cell' 
34204                         }
34205                         
34206                         var markup = ct.apply(p);
34207                         if(!c.locked){
34208                             cb[cb.length] = markup;
34209                         }else{
34210                             lcb[lcb.length] = markup;
34211                         }
34212                     }
34213                     var alt = [];
34214                     if(stripe && ((rowIndex+1) % 2 == 0)){
34215                         alt.push( "x-grid-row-alt");
34216                     }
34217                     if(r.dirty){
34218                         alt.push(" x-grid-dirty-row");
34219                     }
34220                     rp.cells = lcb;
34221                     if(this.getRowClass){
34222                         alt.push( this.getRowClass(r, rowIndex));
34223                     }
34224                     if (hasListener) {
34225                         rowcfg = {
34226                              
34227                             record: r,
34228                             rowIndex : rowIndex,
34229                             rowClass : ''
34230                         };
34231                         this.grid.fireEvent('rowclass', this, rowcfg);
34232                         alt.push(rowcfg.rowClass);
34233                     }
34234                     
34235                     rp.alt = alt.join(" ");
34236                     rp.cells = lcb.join("");
34237                     lbuf[lbuf.length] = rt.apply(rp);
34238                     rp.cells = cb.join("");
34239                     buf[buf.length] =  rt.apply(rp);
34240                 }
34241                 return [lbuf.join(""), buf.join("")];
34242             },
34243
34244     renderBody : function(){
34245         var markup = this.renderRows();
34246         var bt = this.templates.body;
34247         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34248     },
34249
34250     /**
34251      * Refreshes the grid
34252      * @param {Boolean} headersToo
34253      */
34254     refresh : function(headersToo){
34255         this.fireEvent("beforerefresh", this);
34256         this.grid.stopEditing();
34257         var result = this.renderBody();
34258         this.lockedBody.update(result[0]);
34259         this.mainBody.update(result[1]);
34260         if(headersToo === true){
34261             this.updateHeaders();
34262             this.updateColumns();
34263             this.updateSplitters();
34264             this.updateHeaderSortState();
34265         }
34266         this.syncRowHeights();
34267         this.layout();
34268         this.fireEvent("refresh", this);
34269     },
34270
34271     handleColumnMove : function(cm, oldIndex, newIndex){
34272         this.indexMap = null;
34273         var s = this.getScrollState();
34274         this.refresh(true);
34275         this.restoreScroll(s);
34276         this.afterMove(newIndex);
34277     },
34278
34279     afterMove : function(colIndex){
34280         if(this.enableMoveAnim && Roo.enableFx){
34281             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34282         }
34283         // if multisort - fix sortOrder, and reload..
34284         if (this.grid.dataSource.multiSort) {
34285             // the we can call sort again..
34286             var dm = this.grid.dataSource;
34287             var cm = this.grid.colModel;
34288             var so = [];
34289             for(var i = 0; i < cm.config.length; i++ ) {
34290                 
34291                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34292                     continue; // dont' bother, it's not in sort list or being set.
34293                 }
34294                 
34295                 so.push(cm.config[i].dataIndex);
34296             };
34297             dm.sortOrder = so;
34298             dm.load(dm.lastOptions);
34299             
34300             
34301         }
34302         
34303     },
34304
34305     updateCell : function(dm, rowIndex, dataIndex){
34306         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34307         if(typeof colIndex == "undefined"){ // not present in grid
34308             return;
34309         }
34310         var cm = this.grid.colModel;
34311         var cell = this.getCell(rowIndex, colIndex);
34312         var cellText = this.getCellText(rowIndex, colIndex);
34313
34314         var p = {
34315             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34316             id : cm.getColumnId(colIndex),
34317             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34318         };
34319         var renderer = cm.getRenderer(colIndex);
34320         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34321         if(typeof val == "undefined" || val === "") {
34322             val = "&#160;";
34323         }
34324         cellText.innerHTML = val;
34325         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34326         this.syncRowHeights(rowIndex, rowIndex);
34327     },
34328
34329     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34330         var maxWidth = 0;
34331         if(this.grid.autoSizeHeaders){
34332             var h = this.getHeaderCellMeasure(colIndex);
34333             maxWidth = Math.max(maxWidth, h.scrollWidth);
34334         }
34335         var tb, index;
34336         if(this.cm.isLocked(colIndex)){
34337             tb = this.getLockedTable();
34338             index = colIndex;
34339         }else{
34340             tb = this.getBodyTable();
34341             index = colIndex - this.cm.getLockedCount();
34342         }
34343         if(tb && tb.rows){
34344             var rows = tb.rows;
34345             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34346             for(var i = 0; i < stopIndex; i++){
34347                 var cell = rows[i].childNodes[index].firstChild;
34348                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34349             }
34350         }
34351         return maxWidth + /*margin for error in IE*/ 5;
34352     },
34353     /**
34354      * Autofit a column to its content.
34355      * @param {Number} colIndex
34356      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34357      */
34358      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34359          if(this.cm.isHidden(colIndex)){
34360              return; // can't calc a hidden column
34361          }
34362         if(forceMinSize){
34363             var cid = this.cm.getColumnId(colIndex);
34364             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34365            if(this.grid.autoSizeHeaders){
34366                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34367            }
34368         }
34369         var newWidth = this.calcColumnWidth(colIndex);
34370         this.cm.setColumnWidth(colIndex,
34371             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34372         if(!suppressEvent){
34373             this.grid.fireEvent("columnresize", colIndex, newWidth);
34374         }
34375     },
34376
34377     /**
34378      * Autofits all columns to their content and then expands to fit any extra space in the grid
34379      */
34380      autoSizeColumns : function(){
34381         var cm = this.grid.colModel;
34382         var colCount = cm.getColumnCount();
34383         for(var i = 0; i < colCount; i++){
34384             this.autoSizeColumn(i, true, true);
34385         }
34386         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34387             this.fitColumns();
34388         }else{
34389             this.updateColumns();
34390             this.layout();
34391         }
34392     },
34393
34394     /**
34395      * Autofits all columns to the grid's width proportionate with their current size
34396      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34397      */
34398     fitColumns : function(reserveScrollSpace){
34399         var cm = this.grid.colModel;
34400         var colCount = cm.getColumnCount();
34401         var cols = [];
34402         var width = 0;
34403         var i, w;
34404         for (i = 0; i < colCount; i++){
34405             if(!cm.isHidden(i) && !cm.isFixed(i)){
34406                 w = cm.getColumnWidth(i);
34407                 cols.push(i);
34408                 cols.push(w);
34409                 width += w;
34410             }
34411         }
34412         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34413         if(reserveScrollSpace){
34414             avail -= 17;
34415         }
34416         var frac = (avail - cm.getTotalWidth())/width;
34417         while (cols.length){
34418             w = cols.pop();
34419             i = cols.pop();
34420             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34421         }
34422         this.updateColumns();
34423         this.layout();
34424     },
34425
34426     onRowSelect : function(rowIndex){
34427         var row = this.getRowComposite(rowIndex);
34428         row.addClass("x-grid-row-selected");
34429     },
34430
34431     onRowDeselect : function(rowIndex){
34432         var row = this.getRowComposite(rowIndex);
34433         row.removeClass("x-grid-row-selected");
34434     },
34435
34436     onCellSelect : function(row, col){
34437         var cell = this.getCell(row, col);
34438         if(cell){
34439             Roo.fly(cell).addClass("x-grid-cell-selected");
34440         }
34441     },
34442
34443     onCellDeselect : function(row, col){
34444         var cell = this.getCell(row, col);
34445         if(cell){
34446             Roo.fly(cell).removeClass("x-grid-cell-selected");
34447         }
34448     },
34449
34450     updateHeaderSortState : function(){
34451         
34452         // sort state can be single { field: xxx, direction : yyy}
34453         // or   { xxx=>ASC , yyy : DESC ..... }
34454         
34455         var mstate = {};
34456         if (!this.ds.multiSort) { 
34457             var state = this.ds.getSortState();
34458             if(!state){
34459                 return;
34460             }
34461             mstate[state.field] = state.direction;
34462             // FIXME... - this is not used here.. but might be elsewhere..
34463             this.sortState = state;
34464             
34465         } else {
34466             mstate = this.ds.sortToggle;
34467         }
34468         //remove existing sort classes..
34469         
34470         var sc = this.sortClasses;
34471         var hds = this.el.select(this.headerSelector).removeClass(sc);
34472         
34473         for(var f in mstate) {
34474         
34475             var sortColumn = this.cm.findColumnIndex(f);
34476             
34477             if(sortColumn != -1){
34478                 var sortDir = mstate[f];        
34479                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34480             }
34481         }
34482         
34483          
34484         
34485     },
34486
34487
34488     handleHeaderClick : function(g, index,e){
34489         
34490         Roo.log("header click");
34491         
34492         if (Roo.isTouch) {
34493             // touch events on header are handled by context
34494             this.handleHdCtx(g,index,e);
34495             return;
34496         }
34497         
34498         
34499         if(this.headersDisabled){
34500             return;
34501         }
34502         var dm = g.dataSource, cm = g.colModel;
34503         if(!cm.isSortable(index)){
34504             return;
34505         }
34506         g.stopEditing();
34507         
34508         if (dm.multiSort) {
34509             // update the sortOrder
34510             var so = [];
34511             for(var i = 0; i < cm.config.length; i++ ) {
34512                 
34513                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34514                     continue; // dont' bother, it's not in sort list or being set.
34515                 }
34516                 
34517                 so.push(cm.config[i].dataIndex);
34518             };
34519             dm.sortOrder = so;
34520         }
34521         
34522         
34523         dm.sort(cm.getDataIndex(index));
34524     },
34525
34526
34527     destroy : function(){
34528         if(this.colMenu){
34529             this.colMenu.removeAll();
34530             Roo.menu.MenuMgr.unregister(this.colMenu);
34531             this.colMenu.getEl().remove();
34532             delete this.colMenu;
34533         }
34534         if(this.hmenu){
34535             this.hmenu.removeAll();
34536             Roo.menu.MenuMgr.unregister(this.hmenu);
34537             this.hmenu.getEl().remove();
34538             delete this.hmenu;
34539         }
34540         if(this.grid.enableColumnMove){
34541             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34542             if(dds){
34543                 for(var dd in dds){
34544                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34545                         var elid = dds[dd].dragElId;
34546                         dds[dd].unreg();
34547                         Roo.get(elid).remove();
34548                     } else if(dds[dd].config.isTarget){
34549                         dds[dd].proxyTop.remove();
34550                         dds[dd].proxyBottom.remove();
34551                         dds[dd].unreg();
34552                     }
34553                     if(Roo.dd.DDM.locationCache[dd]){
34554                         delete Roo.dd.DDM.locationCache[dd];
34555                     }
34556                 }
34557                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34558             }
34559         }
34560         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34561         this.bind(null, null);
34562         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34563     },
34564
34565     handleLockChange : function(){
34566         this.refresh(true);
34567     },
34568
34569     onDenyColumnLock : function(){
34570
34571     },
34572
34573     onDenyColumnHide : function(){
34574
34575     },
34576
34577     handleHdMenuClick : function(item){
34578         var index = this.hdCtxIndex;
34579         var cm = this.cm, ds = this.ds;
34580         switch(item.id){
34581             case "asc":
34582                 ds.sort(cm.getDataIndex(index), "ASC");
34583                 break;
34584             case "desc":
34585                 ds.sort(cm.getDataIndex(index), "DESC");
34586                 break;
34587             case "lock":
34588                 var lc = cm.getLockedCount();
34589                 if(cm.getColumnCount(true) <= lc+1){
34590                     this.onDenyColumnLock();
34591                     return;
34592                 }
34593                 if(lc != index){
34594                     cm.setLocked(index, true, true);
34595                     cm.moveColumn(index, lc);
34596                     this.grid.fireEvent("columnmove", index, lc);
34597                 }else{
34598                     cm.setLocked(index, true);
34599                 }
34600             break;
34601             case "unlock":
34602                 var lc = cm.getLockedCount();
34603                 if((lc-1) != index){
34604                     cm.setLocked(index, false, true);
34605                     cm.moveColumn(index, lc-1);
34606                     this.grid.fireEvent("columnmove", index, lc-1);
34607                 }else{
34608                     cm.setLocked(index, false);
34609                 }
34610             break;
34611             case 'wider': // used to expand cols on touch..
34612             case 'narrow':
34613                 var cw = cm.getColumnWidth(index);
34614                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34615                 cw = Math.max(0, cw);
34616                 cw = Math.min(cw,4000);
34617                 cm.setColumnWidth(index, cw);
34618                 break;
34619                 
34620             default:
34621                 index = cm.getIndexById(item.id.substr(4));
34622                 if(index != -1){
34623                     if(item.checked && cm.getColumnCount(true) <= 1){
34624                         this.onDenyColumnHide();
34625                         return false;
34626                     }
34627                     cm.setHidden(index, item.checked);
34628                 }
34629         }
34630         return true;
34631     },
34632
34633     beforeColMenuShow : function(){
34634         var cm = this.cm,  colCount = cm.getColumnCount();
34635         this.colMenu.removeAll();
34636         for(var i = 0; i < colCount; i++){
34637             this.colMenu.add(new Roo.menu.CheckItem({
34638                 id: "col-"+cm.getColumnId(i),
34639                 text: cm.getColumnHeader(i),
34640                 checked: !cm.isHidden(i),
34641                 hideOnClick:false
34642             }));
34643         }
34644     },
34645
34646     handleHdCtx : function(g, index, e){
34647         e.stopEvent();
34648         var hd = this.getHeaderCell(index);
34649         this.hdCtxIndex = index;
34650         var ms = this.hmenu.items, cm = this.cm;
34651         ms.get("asc").setDisabled(!cm.isSortable(index));
34652         ms.get("desc").setDisabled(!cm.isSortable(index));
34653         if(this.grid.enableColLock !== false){
34654             ms.get("lock").setDisabled(cm.isLocked(index));
34655             ms.get("unlock").setDisabled(!cm.isLocked(index));
34656         }
34657         this.hmenu.show(hd, "tl-bl");
34658     },
34659
34660     handleHdOver : function(e){
34661         var hd = this.findHeaderCell(e.getTarget());
34662         if(hd && !this.headersDisabled){
34663             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34664                this.fly(hd).addClass("x-grid-hd-over");
34665             }
34666         }
34667     },
34668
34669     handleHdOut : function(e){
34670         var hd = this.findHeaderCell(e.getTarget());
34671         if(hd){
34672             this.fly(hd).removeClass("x-grid-hd-over");
34673         }
34674     },
34675
34676     handleSplitDblClick : function(e, t){
34677         var i = this.getCellIndex(t);
34678         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34679             this.autoSizeColumn(i, true);
34680             this.layout();
34681         }
34682     },
34683
34684     render : function(){
34685
34686         var cm = this.cm;
34687         var colCount = cm.getColumnCount();
34688
34689         if(this.grid.monitorWindowResize === true){
34690             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34691         }
34692         var header = this.renderHeaders();
34693         var body = this.templates.body.apply({rows:""});
34694         var html = this.templates.master.apply({
34695             lockedBody: body,
34696             body: body,
34697             lockedHeader: header[0],
34698             header: header[1]
34699         });
34700
34701         //this.updateColumns();
34702
34703         this.grid.getGridEl().dom.innerHTML = html;
34704
34705         this.initElements();
34706         
34707         // a kludge to fix the random scolling effect in webkit
34708         this.el.on("scroll", function() {
34709             this.el.dom.scrollTop=0; // hopefully not recursive..
34710         },this);
34711
34712         this.scroller.on("scroll", this.handleScroll, this);
34713         this.lockedBody.on("mousewheel", this.handleWheel, this);
34714         this.mainBody.on("mousewheel", this.handleWheel, this);
34715
34716         this.mainHd.on("mouseover", this.handleHdOver, this);
34717         this.mainHd.on("mouseout", this.handleHdOut, this);
34718         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34719                 {delegate: "."+this.splitClass});
34720
34721         this.lockedHd.on("mouseover", this.handleHdOver, this);
34722         this.lockedHd.on("mouseout", this.handleHdOut, this);
34723         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34724                 {delegate: "."+this.splitClass});
34725
34726         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34727             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34728         }
34729
34730         this.updateSplitters();
34731
34732         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34733             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34734             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34735         }
34736
34737         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34738             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34739             this.hmenu.add(
34740                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34741                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34742             );
34743             if(this.grid.enableColLock !== false){
34744                 this.hmenu.add('-',
34745                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34746                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34747                 );
34748             }
34749             if (Roo.isTouch) {
34750                  this.hmenu.add('-',
34751                     {id:"wider", text: this.columnsWiderText},
34752                     {id:"narrow", text: this.columnsNarrowText }
34753                 );
34754                 
34755                  
34756             }
34757             
34758             if(this.grid.enableColumnHide !== false){
34759
34760                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34761                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34762                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34763
34764                 this.hmenu.add('-',
34765                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34766                 );
34767             }
34768             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34769
34770             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34771         }
34772
34773         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34774             this.dd = new Roo.grid.GridDragZone(this.grid, {
34775                 ddGroup : this.grid.ddGroup || 'GridDD'
34776             });
34777             
34778         }
34779
34780         /*
34781         for(var i = 0; i < colCount; i++){
34782             if(cm.isHidden(i)){
34783                 this.hideColumn(i);
34784             }
34785             if(cm.config[i].align){
34786                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34787                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34788             }
34789         }*/
34790         
34791         this.updateHeaderSortState();
34792
34793         this.beforeInitialResize();
34794         this.layout(true);
34795
34796         // two part rendering gives faster view to the user
34797         this.renderPhase2.defer(1, this);
34798     },
34799
34800     renderPhase2 : function(){
34801         // render the rows now
34802         this.refresh();
34803         if(this.grid.autoSizeColumns){
34804             this.autoSizeColumns();
34805         }
34806     },
34807
34808     beforeInitialResize : function(){
34809
34810     },
34811
34812     onColumnSplitterMoved : function(i, w){
34813         this.userResized = true;
34814         var cm = this.grid.colModel;
34815         cm.setColumnWidth(i, w, true);
34816         var cid = cm.getColumnId(i);
34817         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34818         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34819         this.updateSplitters();
34820         this.layout();
34821         this.grid.fireEvent("columnresize", i, w);
34822     },
34823
34824     syncRowHeights : function(startIndex, endIndex){
34825         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34826             startIndex = startIndex || 0;
34827             var mrows = this.getBodyTable().rows;
34828             var lrows = this.getLockedTable().rows;
34829             var len = mrows.length-1;
34830             endIndex = Math.min(endIndex || len, len);
34831             for(var i = startIndex; i <= endIndex; i++){
34832                 var m = mrows[i], l = lrows[i];
34833                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34834                 m.style.height = l.style.height = h + "px";
34835             }
34836         }
34837     },
34838
34839     layout : function(initialRender, is2ndPass)
34840     {
34841         var g = this.grid;
34842         var auto = g.autoHeight;
34843         var scrollOffset = 16;
34844         var c = g.getGridEl(), cm = this.cm,
34845                 expandCol = g.autoExpandColumn,
34846                 gv = this;
34847         //c.beginMeasure();
34848
34849         if(!c.dom.offsetWidth){ // display:none?
34850             if(initialRender){
34851                 this.lockedWrap.show();
34852                 this.mainWrap.show();
34853             }
34854             return;
34855         }
34856
34857         var hasLock = this.cm.isLocked(0);
34858
34859         var tbh = this.headerPanel.getHeight();
34860         var bbh = this.footerPanel.getHeight();
34861
34862         if(auto){
34863             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34864             var newHeight = ch + c.getBorderWidth("tb");
34865             if(g.maxHeight){
34866                 newHeight = Math.min(g.maxHeight, newHeight);
34867             }
34868             c.setHeight(newHeight);
34869         }
34870
34871         if(g.autoWidth){
34872             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34873         }
34874
34875         var s = this.scroller;
34876
34877         var csize = c.getSize(true);
34878
34879         this.el.setSize(csize.width, csize.height);
34880
34881         this.headerPanel.setWidth(csize.width);
34882         this.footerPanel.setWidth(csize.width);
34883
34884         var hdHeight = this.mainHd.getHeight();
34885         var vw = csize.width;
34886         var vh = csize.height - (tbh + bbh);
34887
34888         s.setSize(vw, vh);
34889
34890         var bt = this.getBodyTable();
34891         
34892         if(cm.getLockedCount() == cm.config.length){
34893             bt = this.getLockedTable();
34894         }
34895         
34896         var ltWidth = hasLock ?
34897                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34898
34899         var scrollHeight = bt.offsetHeight;
34900         var scrollWidth = ltWidth + bt.offsetWidth;
34901         var vscroll = false, hscroll = false;
34902
34903         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34904
34905         var lw = this.lockedWrap, mw = this.mainWrap;
34906         var lb = this.lockedBody, mb = this.mainBody;
34907
34908         setTimeout(function(){
34909             var t = s.dom.offsetTop;
34910             var w = s.dom.clientWidth,
34911                 h = s.dom.clientHeight;
34912
34913             lw.setTop(t);
34914             lw.setSize(ltWidth, h);
34915
34916             mw.setLeftTop(ltWidth, t);
34917             mw.setSize(w-ltWidth, h);
34918
34919             lb.setHeight(h-hdHeight);
34920             mb.setHeight(h-hdHeight);
34921
34922             if(is2ndPass !== true && !gv.userResized && expandCol){
34923                 // high speed resize without full column calculation
34924                 
34925                 var ci = cm.getIndexById(expandCol);
34926                 if (ci < 0) {
34927                     ci = cm.findColumnIndex(expandCol);
34928                 }
34929                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34930                 var expandId = cm.getColumnId(ci);
34931                 var  tw = cm.getTotalWidth(false);
34932                 var currentWidth = cm.getColumnWidth(ci);
34933                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34934                 if(currentWidth != cw){
34935                     cm.setColumnWidth(ci, cw, true);
34936                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34937                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34938                     gv.updateSplitters();
34939                     gv.layout(false, true);
34940                 }
34941             }
34942
34943             if(initialRender){
34944                 lw.show();
34945                 mw.show();
34946             }
34947             //c.endMeasure();
34948         }, 10);
34949     },
34950
34951     onWindowResize : function(){
34952         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34953             return;
34954         }
34955         this.layout();
34956     },
34957
34958     appendFooter : function(parentEl){
34959         return null;
34960     },
34961
34962     sortAscText : "Sort Ascending",
34963     sortDescText : "Sort Descending",
34964     lockText : "Lock Column",
34965     unlockText : "Unlock Column",
34966     columnsText : "Columns",
34967  
34968     columnsWiderText : "Wider",
34969     columnsNarrowText : "Thinner"
34970 });
34971
34972
34973 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34974     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34975     this.proxy.el.addClass('x-grid3-col-dd');
34976 };
34977
34978 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34979     handleMouseDown : function(e){
34980
34981     },
34982
34983     callHandleMouseDown : function(e){
34984         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34985     }
34986 });
34987 /*
34988  * Based on:
34989  * Ext JS Library 1.1.1
34990  * Copyright(c) 2006-2007, Ext JS, LLC.
34991  *
34992  * Originally Released Under LGPL - original licence link has changed is not relivant.
34993  *
34994  * Fork - LGPL
34995  * <script type="text/javascript">
34996  */
34997  /**
34998  * @extends Roo.dd.DDProxy
34999  * @class Roo.grid.SplitDragZone
35000  * Support for Column Header resizing
35001  * @constructor
35002  * @param {Object} config
35003  */
35004 // private
35005 // This is a support class used internally by the Grid components
35006 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35007     this.grid = grid;
35008     this.view = grid.getView();
35009     this.proxy = this.view.resizeProxy;
35010     Roo.grid.SplitDragZone.superclass.constructor.call(
35011         this,
35012         hd, // ID
35013         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35014         {  // CONFIG
35015             dragElId : Roo.id(this.proxy.dom),
35016             resizeFrame:false
35017         }
35018     );
35019     
35020     this.setHandleElId(Roo.id(hd));
35021     if (hd2 !== false) {
35022         this.setOuterHandleElId(Roo.id(hd2));
35023     }
35024     
35025     this.scroll = false;
35026 };
35027 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35028     fly: Roo.Element.fly,
35029
35030     b4StartDrag : function(x, y){
35031         this.view.headersDisabled = true;
35032         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35033                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35034         );
35035         this.proxy.setHeight(h);
35036         
35037         // for old system colWidth really stored the actual width?
35038         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35039         // which in reality did not work.. - it worked only for fixed sizes
35040         // for resizable we need to use actual sizes.
35041         var w = this.cm.getColumnWidth(this.cellIndex);
35042         if (!this.view.mainWrap) {
35043             // bootstrap.
35044             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35045         }
35046         
35047         
35048         
35049         // this was w-this.grid.minColumnWidth;
35050         // doesnt really make sense? - w = thie curren width or the rendered one?
35051         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35052         this.resetConstraints();
35053         this.setXConstraint(minw, 1000);
35054         this.setYConstraint(0, 0);
35055         this.minX = x - minw;
35056         this.maxX = x + 1000;
35057         this.startPos = x;
35058         if (!this.view.mainWrap) { // this is Bootstrap code..
35059             this.getDragEl().style.display='block';
35060         }
35061         
35062         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35063     },
35064
35065
35066     handleMouseDown : function(e){
35067         ev = Roo.EventObject.setEvent(e);
35068         var t = this.fly(ev.getTarget());
35069         if(t.hasClass("x-grid-split")){
35070             this.cellIndex = this.view.getCellIndex(t.dom);
35071             this.split = t.dom;
35072             this.cm = this.grid.colModel;
35073             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35074                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35075             }
35076         }
35077     },
35078
35079     endDrag : function(e){
35080         this.view.headersDisabled = false;
35081         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35082         var diff = endX - this.startPos;
35083         // 
35084         var w = this.cm.getColumnWidth(this.cellIndex);
35085         if (!this.view.mainWrap) {
35086             w = 0;
35087         }
35088         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35089     },
35090
35091     autoOffset : function(){
35092         this.setDelta(0,0);
35093     }
35094 });/*
35095  * Based on:
35096  * Ext JS Library 1.1.1
35097  * Copyright(c) 2006-2007, Ext JS, LLC.
35098  *
35099  * Originally Released Under LGPL - original licence link has changed is not relivant.
35100  *
35101  * Fork - LGPL
35102  * <script type="text/javascript">
35103  */
35104  
35105 // private
35106 // This is a support class used internally by the Grid components
35107 Roo.grid.GridDragZone = function(grid, config){
35108     this.view = grid.getView();
35109     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35110     if(this.view.lockedBody){
35111         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35112         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35113     }
35114     this.scroll = false;
35115     this.grid = grid;
35116     this.ddel = document.createElement('div');
35117     this.ddel.className = 'x-grid-dd-wrap';
35118 };
35119
35120 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35121     ddGroup : "GridDD",
35122
35123     getDragData : function(e){
35124         var t = Roo.lib.Event.getTarget(e);
35125         var rowIndex = this.view.findRowIndex(t);
35126         var sm = this.grid.selModel;
35127             
35128         //Roo.log(rowIndex);
35129         
35130         if (sm.getSelectedCell) {
35131             // cell selection..
35132             if (!sm.getSelectedCell()) {
35133                 return false;
35134             }
35135             if (rowIndex != sm.getSelectedCell()[0]) {
35136                 return false;
35137             }
35138         
35139         }
35140         if (sm.getSelections && sm.getSelections().length < 1) {
35141             return false;
35142         }
35143         
35144         
35145         // before it used to all dragging of unseleted... - now we dont do that.
35146         if(rowIndex !== false){
35147             
35148             // if editorgrid.. 
35149             
35150             
35151             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35152                
35153             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35154               //  
35155             //}
35156             if (e.hasModifier()){
35157                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35158             }
35159             
35160             Roo.log("getDragData");
35161             
35162             return {
35163                 grid: this.grid,
35164                 ddel: this.ddel,
35165                 rowIndex: rowIndex,
35166                 selections: sm.getSelections ? sm.getSelections() : (
35167                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35168             };
35169         }
35170         return false;
35171     },
35172     
35173     
35174     onInitDrag : function(e){
35175         var data = this.dragData;
35176         this.ddel.innerHTML = this.grid.getDragDropText();
35177         this.proxy.update(this.ddel);
35178         // fire start drag?
35179     },
35180
35181     afterRepair : function(){
35182         this.dragging = false;
35183     },
35184
35185     getRepairXY : function(e, data){
35186         return false;
35187     },
35188
35189     onEndDrag : function(data, e){
35190         // fire end drag?
35191     },
35192
35193     onValidDrop : function(dd, e, id){
35194         // fire drag drop?
35195         this.hideProxy();
35196     },
35197
35198     beforeInvalidDrop : function(e, id){
35199
35200     }
35201 });/*
35202  * Based on:
35203  * Ext JS Library 1.1.1
35204  * Copyright(c) 2006-2007, Ext JS, LLC.
35205  *
35206  * Originally Released Under LGPL - original licence link has changed is not relivant.
35207  *
35208  * Fork - LGPL
35209  * <script type="text/javascript">
35210  */
35211  
35212
35213 /**
35214  * @class Roo.grid.ColumnModel
35215  * @extends Roo.util.Observable
35216  * This is the default implementation of a ColumnModel used by the Grid. It defines
35217  * the columns in the grid.
35218  * <br>Usage:<br>
35219  <pre><code>
35220  var colModel = new Roo.grid.ColumnModel([
35221         {header: "Ticker", width: 60, sortable: true, locked: true},
35222         {header: "Company Name", width: 150, sortable: true},
35223         {header: "Market Cap.", width: 100, sortable: true},
35224         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35225         {header: "Employees", width: 100, sortable: true, resizable: false}
35226  ]);
35227  </code></pre>
35228  * <p>
35229  
35230  * The config options listed for this class are options which may appear in each
35231  * individual column definition.
35232  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35233  * @constructor
35234  * @param {Object} config An Array of column config objects. See this class's
35235  * config objects for details.
35236 */
35237 Roo.grid.ColumnModel = function(config){
35238         /**
35239      * The config passed into the constructor
35240      */
35241     this.config = []; //config;
35242     this.lookup = {};
35243
35244     // if no id, create one
35245     // if the column does not have a dataIndex mapping,
35246     // map it to the order it is in the config
35247     for(var i = 0, len = config.length; i < len; i++){
35248         this.addColumn(config[i]);
35249         
35250     }
35251
35252     /**
35253      * The width of columns which have no width specified (defaults to 100)
35254      * @type Number
35255      */
35256     this.defaultWidth = 100;
35257
35258     /**
35259      * Default sortable of columns which have no sortable specified (defaults to false)
35260      * @type Boolean
35261      */
35262     this.defaultSortable = false;
35263
35264     this.addEvents({
35265         /**
35266              * @event widthchange
35267              * Fires when the width of a column changes.
35268              * @param {ColumnModel} this
35269              * @param {Number} columnIndex The column index
35270              * @param {Number} newWidth The new width
35271              */
35272             "widthchange": true,
35273         /**
35274              * @event headerchange
35275              * Fires when the text of a header changes.
35276              * @param {ColumnModel} this
35277              * @param {Number} columnIndex The column index
35278              * @param {Number} newText The new header text
35279              */
35280             "headerchange": true,
35281         /**
35282              * @event hiddenchange
35283              * Fires when a column is hidden or "unhidden".
35284              * @param {ColumnModel} this
35285              * @param {Number} columnIndex The column index
35286              * @param {Boolean} hidden true if hidden, false otherwise
35287              */
35288             "hiddenchange": true,
35289             /**
35290          * @event columnmoved
35291          * Fires when a column is moved.
35292          * @param {ColumnModel} this
35293          * @param {Number} oldIndex
35294          * @param {Number} newIndex
35295          */
35296         "columnmoved" : true,
35297         /**
35298          * @event columlockchange
35299          * Fires when a column's locked state is changed
35300          * @param {ColumnModel} this
35301          * @param {Number} colIndex
35302          * @param {Boolean} locked true if locked
35303          */
35304         "columnlockchange" : true
35305     });
35306     Roo.grid.ColumnModel.superclass.constructor.call(this);
35307 };
35308 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35309     /**
35310      * @cfg {String} header The header text to display in the Grid view.
35311      */
35312         /**
35313      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35314      */
35315         /**
35316      * @cfg {String} smHeader Header at Bootsrap Small width
35317      */
35318         /**
35319      * @cfg {String} mdHeader Header at Bootsrap Medium width
35320      */
35321         /**
35322      * @cfg {String} lgHeader Header at Bootsrap Large width
35323      */
35324         /**
35325      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35326      */
35327     /**
35328      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35329      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35330      * specified, the column's index is used as an index into the Record's data Array.
35331      */
35332     /**
35333      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35334      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35335      */
35336     /**
35337      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35338      * Defaults to the value of the {@link #defaultSortable} property.
35339      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35340      */
35341     /**
35342      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35343      */
35344     /**
35345      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35346      */
35347     /**
35348      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35349      */
35350     /**
35351      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35352      */
35353     /**
35354      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35355      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35356      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35357      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35358      */
35359        /**
35360      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35361      */
35362     /**
35363      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35364      */
35365     /**
35366      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35367      */
35368     /**
35369      * @cfg {String} cursor (Optional)
35370      */
35371     /**
35372      * @cfg {String} tooltip (Optional)
35373      */
35374     /**
35375      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35376      */
35377     /**
35378      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35379      */
35380     /**
35381      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35382      */
35383     /**
35384      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35385      */
35386         /**
35387      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35388      */
35389     /**
35390      * Returns the id of the column at the specified index.
35391      * @param {Number} index The column index
35392      * @return {String} the id
35393      */
35394     getColumnId : function(index){
35395         return this.config[index].id;
35396     },
35397
35398     /**
35399      * Returns the column for a specified id.
35400      * @param {String} id The column id
35401      * @return {Object} the column
35402      */
35403     getColumnById : function(id){
35404         return this.lookup[id];
35405     },
35406
35407     
35408     /**
35409      * Returns the column Object for a specified dataIndex.
35410      * @param {String} dataIndex The column dataIndex
35411      * @return {Object|Boolean} the column or false if not found
35412      */
35413     getColumnByDataIndex: function(dataIndex){
35414         var index = this.findColumnIndex(dataIndex);
35415         return index > -1 ? this.config[index] : false;
35416     },
35417     
35418     /**
35419      * Returns the index for a specified column id.
35420      * @param {String} id The column id
35421      * @return {Number} the index, or -1 if not found
35422      */
35423     getIndexById : function(id){
35424         for(var i = 0, len = this.config.length; i < len; i++){
35425             if(this.config[i].id == id){
35426                 return i;
35427             }
35428         }
35429         return -1;
35430     },
35431     
35432     /**
35433      * Returns the index for a specified column dataIndex.
35434      * @param {String} dataIndex The column dataIndex
35435      * @return {Number} the index, or -1 if not found
35436      */
35437     
35438     findColumnIndex : function(dataIndex){
35439         for(var i = 0, len = this.config.length; i < len; i++){
35440             if(this.config[i].dataIndex == dataIndex){
35441                 return i;
35442             }
35443         }
35444         return -1;
35445     },
35446     
35447     
35448     moveColumn : function(oldIndex, newIndex){
35449         var c = this.config[oldIndex];
35450         this.config.splice(oldIndex, 1);
35451         this.config.splice(newIndex, 0, c);
35452         this.dataMap = null;
35453         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35454     },
35455
35456     isLocked : function(colIndex){
35457         return this.config[colIndex].locked === true;
35458     },
35459
35460     setLocked : function(colIndex, value, suppressEvent){
35461         if(this.isLocked(colIndex) == value){
35462             return;
35463         }
35464         this.config[colIndex].locked = value;
35465         if(!suppressEvent){
35466             this.fireEvent("columnlockchange", this, colIndex, value);
35467         }
35468     },
35469
35470     getTotalLockedWidth : function(){
35471         var totalWidth = 0;
35472         for(var i = 0; i < this.config.length; i++){
35473             if(this.isLocked(i) && !this.isHidden(i)){
35474                 this.totalWidth += this.getColumnWidth(i);
35475             }
35476         }
35477         return totalWidth;
35478     },
35479
35480     getLockedCount : function(){
35481         for(var i = 0, len = this.config.length; i < len; i++){
35482             if(!this.isLocked(i)){
35483                 return i;
35484             }
35485         }
35486         
35487         return this.config.length;
35488     },
35489
35490     /**
35491      * Returns the number of columns.
35492      * @return {Number}
35493      */
35494     getColumnCount : function(visibleOnly){
35495         if(visibleOnly === true){
35496             var c = 0;
35497             for(var i = 0, len = this.config.length; i < len; i++){
35498                 if(!this.isHidden(i)){
35499                     c++;
35500                 }
35501             }
35502             return c;
35503         }
35504         return this.config.length;
35505     },
35506
35507     /**
35508      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35509      * @param {Function} fn
35510      * @param {Object} scope (optional)
35511      * @return {Array} result
35512      */
35513     getColumnsBy : function(fn, scope){
35514         var r = [];
35515         for(var i = 0, len = this.config.length; i < len; i++){
35516             var c = this.config[i];
35517             if(fn.call(scope||this, c, i) === true){
35518                 r[r.length] = c;
35519             }
35520         }
35521         return r;
35522     },
35523
35524     /**
35525      * Returns true if the specified column is sortable.
35526      * @param {Number} col The column index
35527      * @return {Boolean}
35528      */
35529     isSortable : function(col){
35530         if(typeof this.config[col].sortable == "undefined"){
35531             return this.defaultSortable;
35532         }
35533         return this.config[col].sortable;
35534     },
35535
35536     /**
35537      * Returns the rendering (formatting) function defined for the column.
35538      * @param {Number} col The column index.
35539      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35540      */
35541     getRenderer : function(col){
35542         if(!this.config[col].renderer){
35543             return Roo.grid.ColumnModel.defaultRenderer;
35544         }
35545         return this.config[col].renderer;
35546     },
35547
35548     /**
35549      * Sets the rendering (formatting) function for a column.
35550      * @param {Number} col The column index
35551      * @param {Function} fn The function to use to process the cell's raw data
35552      * to return HTML markup for the grid view. The render function is called with
35553      * the following parameters:<ul>
35554      * <li>Data value.</li>
35555      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35556      * <li>css A CSS style string to apply to the table cell.</li>
35557      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35558      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35559      * <li>Row index</li>
35560      * <li>Column index</li>
35561      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35562      */
35563     setRenderer : function(col, fn){
35564         this.config[col].renderer = fn;
35565     },
35566
35567     /**
35568      * Returns the width for the specified column.
35569      * @param {Number} col The column index
35570      * @param (optional) {String} gridSize bootstrap width size.
35571      * @return {Number}
35572      */
35573     getColumnWidth : function(col, gridSize)
35574         {
35575                 var cfg = this.config[col];
35576                 
35577                 if (typeof(gridSize) == 'undefined') {
35578                         return cfg.width * 1 || this.defaultWidth;
35579                 }
35580                 if (gridSize === false) { // if we set it..
35581                         return cfg.width || false;
35582                 }
35583                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35584                 
35585                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35586                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35587                                 continue;
35588                         }
35589                         return cfg[ sizes[i] ];
35590                 }
35591                 return 1;
35592                 
35593     },
35594
35595     /**
35596      * Sets the width for a column.
35597      * @param {Number} col The column index
35598      * @param {Number} width The new width
35599      */
35600     setColumnWidth : function(col, width, suppressEvent){
35601         this.config[col].width = width;
35602         this.totalWidth = null;
35603         if(!suppressEvent){
35604              this.fireEvent("widthchange", this, col, width);
35605         }
35606     },
35607
35608     /**
35609      * Returns the total width of all columns.
35610      * @param {Boolean} includeHidden True to include hidden column widths
35611      * @return {Number}
35612      */
35613     getTotalWidth : function(includeHidden){
35614         if(!this.totalWidth){
35615             this.totalWidth = 0;
35616             for(var i = 0, len = this.config.length; i < len; i++){
35617                 if(includeHidden || !this.isHidden(i)){
35618                     this.totalWidth += this.getColumnWidth(i);
35619                 }
35620             }
35621         }
35622         return this.totalWidth;
35623     },
35624
35625     /**
35626      * Returns the header for the specified column.
35627      * @param {Number} col The column index
35628      * @return {String}
35629      */
35630     getColumnHeader : function(col){
35631         return this.config[col].header;
35632     },
35633
35634     /**
35635      * Sets the header for a column.
35636      * @param {Number} col The column index
35637      * @param {String} header The new header
35638      */
35639     setColumnHeader : function(col, header){
35640         this.config[col].header = header;
35641         this.fireEvent("headerchange", this, col, header);
35642     },
35643
35644     /**
35645      * Returns the tooltip for the specified column.
35646      * @param {Number} col The column index
35647      * @return {String}
35648      */
35649     getColumnTooltip : function(col){
35650             return this.config[col].tooltip;
35651     },
35652     /**
35653      * Sets the tooltip for a column.
35654      * @param {Number} col The column index
35655      * @param {String} tooltip The new tooltip
35656      */
35657     setColumnTooltip : function(col, tooltip){
35658             this.config[col].tooltip = tooltip;
35659     },
35660
35661     /**
35662      * Returns the dataIndex for the specified column.
35663      * @param {Number} col The column index
35664      * @return {Number}
35665      */
35666     getDataIndex : function(col){
35667         return this.config[col].dataIndex;
35668     },
35669
35670     /**
35671      * Sets the dataIndex for a column.
35672      * @param {Number} col The column index
35673      * @param {Number} dataIndex The new dataIndex
35674      */
35675     setDataIndex : function(col, dataIndex){
35676         this.config[col].dataIndex = dataIndex;
35677     },
35678
35679     
35680     
35681     /**
35682      * Returns true if the cell is editable.
35683      * @param {Number} colIndex The column index
35684      * @param {Number} rowIndex The row index - this is nto actually used..?
35685      * @return {Boolean}
35686      */
35687     isCellEditable : function(colIndex, rowIndex){
35688         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35689     },
35690
35691     /**
35692      * Returns the editor defined for the cell/column.
35693      * return false or null to disable editing.
35694      * @param {Number} colIndex The column index
35695      * @param {Number} rowIndex The row index
35696      * @return {Object}
35697      */
35698     getCellEditor : function(colIndex, rowIndex){
35699         return this.config[colIndex].editor;
35700     },
35701
35702     /**
35703      * Sets if a column is editable.
35704      * @param {Number} col The column index
35705      * @param {Boolean} editable True if the column is editable
35706      */
35707     setEditable : function(col, editable){
35708         this.config[col].editable = editable;
35709     },
35710
35711
35712     /**
35713      * Returns true if the column is hidden.
35714      * @param {Number} colIndex The column index
35715      * @return {Boolean}
35716      */
35717     isHidden : function(colIndex){
35718         return this.config[colIndex].hidden;
35719     },
35720
35721
35722     /**
35723      * Returns true if the column width cannot be changed
35724      */
35725     isFixed : function(colIndex){
35726         return this.config[colIndex].fixed;
35727     },
35728
35729     /**
35730      * Returns true if the column can be resized
35731      * @return {Boolean}
35732      */
35733     isResizable : function(colIndex){
35734         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35735     },
35736     /**
35737      * Sets if a column is hidden.
35738      * @param {Number} colIndex The column index
35739      * @param {Boolean} hidden True if the column is hidden
35740      */
35741     setHidden : function(colIndex, hidden){
35742         this.config[colIndex].hidden = hidden;
35743         this.totalWidth = null;
35744         this.fireEvent("hiddenchange", this, colIndex, hidden);
35745     },
35746
35747     /**
35748      * Sets the editor for a column.
35749      * @param {Number} col The column index
35750      * @param {Object} editor The editor object
35751      */
35752     setEditor : function(col, editor){
35753         this.config[col].editor = editor;
35754     },
35755     /**
35756      * Add a column (experimental...) - defaults to adding to the end..
35757      * @param {Object} config 
35758     */
35759     addColumn : function(c)
35760     {
35761     
35762         var i = this.config.length;
35763         this.config[i] = c;
35764         
35765         if(typeof c.dataIndex == "undefined"){
35766             c.dataIndex = i;
35767         }
35768         if(typeof c.renderer == "string"){
35769             c.renderer = Roo.util.Format[c.renderer];
35770         }
35771         if(typeof c.id == "undefined"){
35772             c.id = Roo.id();
35773         }
35774         if(c.editor && c.editor.xtype){
35775             c.editor  = Roo.factory(c.editor, Roo.grid);
35776         }
35777         if(c.editor && c.editor.isFormField){
35778             c.editor = new Roo.grid.GridEditor(c.editor);
35779         }
35780         this.lookup[c.id] = c;
35781     }
35782     
35783 });
35784
35785 Roo.grid.ColumnModel.defaultRenderer = function(value)
35786 {
35787     if(typeof value == "object") {
35788         return value;
35789     }
35790         if(typeof value == "string" && value.length < 1){
35791             return "&#160;";
35792         }
35793     
35794         return String.format("{0}", value);
35795 };
35796
35797 // Alias for backwards compatibility
35798 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35799 /*
35800  * Based on:
35801  * Ext JS Library 1.1.1
35802  * Copyright(c) 2006-2007, Ext JS, LLC.
35803  *
35804  * Originally Released Under LGPL - original licence link has changed is not relivant.
35805  *
35806  * Fork - LGPL
35807  * <script type="text/javascript">
35808  */
35809
35810 /**
35811  * @class Roo.grid.AbstractSelectionModel
35812  * @extends Roo.util.Observable
35813  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35814  * implemented by descendant classes.  This class should not be directly instantiated.
35815  * @constructor
35816  */
35817 Roo.grid.AbstractSelectionModel = function(){
35818     this.locked = false;
35819     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35820 };
35821
35822 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35823     /** @ignore Called by the grid automatically. Do not call directly. */
35824     init : function(grid){
35825         this.grid = grid;
35826         this.initEvents();
35827     },
35828
35829     /**
35830      * Locks the selections.
35831      */
35832     lock : function(){
35833         this.locked = true;
35834     },
35835
35836     /**
35837      * Unlocks the selections.
35838      */
35839     unlock : function(){
35840         this.locked = false;
35841     },
35842
35843     /**
35844      * Returns true if the selections are locked.
35845      * @return {Boolean}
35846      */
35847     isLocked : function(){
35848         return this.locked;
35849     }
35850 });/*
35851  * Based on:
35852  * Ext JS Library 1.1.1
35853  * Copyright(c) 2006-2007, Ext JS, LLC.
35854  *
35855  * Originally Released Under LGPL - original licence link has changed is not relivant.
35856  *
35857  * Fork - LGPL
35858  * <script type="text/javascript">
35859  */
35860 /**
35861  * @extends Roo.grid.AbstractSelectionModel
35862  * @class Roo.grid.RowSelectionModel
35863  * The default SelectionModel used by {@link Roo.grid.Grid}.
35864  * It supports multiple selections and keyboard selection/navigation. 
35865  * @constructor
35866  * @param {Object} config
35867  */
35868 Roo.grid.RowSelectionModel = function(config){
35869     Roo.apply(this, config);
35870     this.selections = new Roo.util.MixedCollection(false, function(o){
35871         return o.id;
35872     });
35873
35874     this.last = false;
35875     this.lastActive = false;
35876
35877     this.addEvents({
35878         /**
35879         * @event selectionchange
35880         * Fires when the selection changes
35881         * @param {SelectionModel} this
35882         */
35883        "selectionchange" : true,
35884        /**
35885         * @event afterselectionchange
35886         * Fires after the selection changes (eg. by key press or clicking)
35887         * @param {SelectionModel} this
35888         */
35889        "afterselectionchange" : true,
35890        /**
35891         * @event beforerowselect
35892         * Fires when a row is selected being selected, return false to cancel.
35893         * @param {SelectionModel} this
35894         * @param {Number} rowIndex The selected index
35895         * @param {Boolean} keepExisting False if other selections will be cleared
35896         */
35897        "beforerowselect" : true,
35898        /**
35899         * @event rowselect
35900         * Fires when a row is selected.
35901         * @param {SelectionModel} this
35902         * @param {Number} rowIndex The selected index
35903         * @param {Roo.data.Record} r The record
35904         */
35905        "rowselect" : true,
35906        /**
35907         * @event rowdeselect
35908         * Fires when a row is deselected.
35909         * @param {SelectionModel} this
35910         * @param {Number} rowIndex The selected index
35911         */
35912         "rowdeselect" : true
35913     });
35914     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35915     this.locked = false;
35916 };
35917
35918 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35919     /**
35920      * @cfg {Boolean} singleSelect
35921      * True to allow selection of only one row at a time (defaults to false)
35922      */
35923     singleSelect : false,
35924
35925     // private
35926     initEvents : function(){
35927
35928         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35929             this.grid.on("mousedown", this.handleMouseDown, this);
35930         }else{ // allow click to work like normal
35931             this.grid.on("rowclick", this.handleDragableRowClick, this);
35932         }
35933         // bootstrap does not have a view..
35934         var view = this.grid.view ? this.grid.view : this.grid;
35935         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35936             "up" : function(e){
35937                 if(!e.shiftKey){
35938                     this.selectPrevious(e.shiftKey);
35939                 }else if(this.last !== false && this.lastActive !== false){
35940                     var last = this.last;
35941                     this.selectRange(this.last,  this.lastActive-1);
35942                     view.focusRow(this.lastActive);
35943                     if(last !== false){
35944                         this.last = last;
35945                     }
35946                 }else{
35947                     this.selectFirstRow();
35948                 }
35949                 this.fireEvent("afterselectionchange", this);
35950             },
35951             "down" : function(e){
35952                 if(!e.shiftKey){
35953                     this.selectNext(e.shiftKey);
35954                 }else if(this.last !== false && this.lastActive !== false){
35955                     var last = this.last;
35956                     this.selectRange(this.last,  this.lastActive+1);
35957                     view.focusRow(this.lastActive);
35958                     if(last !== false){
35959                         this.last = last;
35960                     }
35961                 }else{
35962                     this.selectFirstRow();
35963                 }
35964                 this.fireEvent("afterselectionchange", this);
35965             },
35966             scope: this
35967         });
35968
35969          
35970         view.on("refresh", this.onRefresh, this);
35971         view.on("rowupdated", this.onRowUpdated, this);
35972         view.on("rowremoved", this.onRemove, this);
35973     },
35974
35975     // private
35976     onRefresh : function(){
35977         var ds = this.grid.ds, i, v = this.grid.view;
35978         var s = this.selections;
35979         s.each(function(r){
35980             if((i = ds.indexOfId(r.id)) != -1){
35981                 v.onRowSelect(i);
35982                 s.add(ds.getAt(i)); // updating the selection relate data
35983             }else{
35984                 s.remove(r);
35985             }
35986         });
35987     },
35988
35989     // private
35990     onRemove : function(v, index, r){
35991         this.selections.remove(r);
35992     },
35993
35994     // private
35995     onRowUpdated : function(v, index, r){
35996         if(this.isSelected(r)){
35997             v.onRowSelect(index);
35998         }
35999     },
36000
36001     /**
36002      * Select records.
36003      * @param {Array} records The records to select
36004      * @param {Boolean} keepExisting (optional) True to keep existing selections
36005      */
36006     selectRecords : function(records, keepExisting){
36007         if(!keepExisting){
36008             this.clearSelections();
36009         }
36010         var ds = this.grid.ds;
36011         for(var i = 0, len = records.length; i < len; i++){
36012             this.selectRow(ds.indexOf(records[i]), true);
36013         }
36014     },
36015
36016     /**
36017      * Gets the number of selected rows.
36018      * @return {Number}
36019      */
36020     getCount : function(){
36021         return this.selections.length;
36022     },
36023
36024     /**
36025      * Selects the first row in the grid.
36026      */
36027     selectFirstRow : function(){
36028         this.selectRow(0);
36029     },
36030
36031     /**
36032      * Select the last row.
36033      * @param {Boolean} keepExisting (optional) True to keep existing selections
36034      */
36035     selectLastRow : function(keepExisting){
36036         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36037     },
36038
36039     /**
36040      * Selects the row immediately following the last selected row.
36041      * @param {Boolean} keepExisting (optional) True to keep existing selections
36042      */
36043     selectNext : function(keepExisting){
36044         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36045             this.selectRow(this.last+1, keepExisting);
36046             var view = this.grid.view ? this.grid.view : this.grid;
36047             view.focusRow(this.last);
36048         }
36049     },
36050
36051     /**
36052      * Selects the row that precedes the last selected row.
36053      * @param {Boolean} keepExisting (optional) True to keep existing selections
36054      */
36055     selectPrevious : function(keepExisting){
36056         if(this.last){
36057             this.selectRow(this.last-1, keepExisting);
36058             var view = this.grid.view ? this.grid.view : this.grid;
36059             view.focusRow(this.last);
36060         }
36061     },
36062
36063     /**
36064      * Returns the selected records
36065      * @return {Array} Array of selected records
36066      */
36067     getSelections : function(){
36068         return [].concat(this.selections.items);
36069     },
36070
36071     /**
36072      * Returns the first selected record.
36073      * @return {Record}
36074      */
36075     getSelected : function(){
36076         return this.selections.itemAt(0);
36077     },
36078
36079
36080     /**
36081      * Clears all selections.
36082      */
36083     clearSelections : function(fast){
36084         if(this.locked) {
36085             return;
36086         }
36087         if(fast !== true){
36088             var ds = this.grid.ds;
36089             var s = this.selections;
36090             s.each(function(r){
36091                 this.deselectRow(ds.indexOfId(r.id));
36092             }, this);
36093             s.clear();
36094         }else{
36095             this.selections.clear();
36096         }
36097         this.last = false;
36098     },
36099
36100
36101     /**
36102      * Selects all rows.
36103      */
36104     selectAll : function(){
36105         if(this.locked) {
36106             return;
36107         }
36108         this.selections.clear();
36109         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36110             this.selectRow(i, true);
36111         }
36112     },
36113
36114     /**
36115      * Returns True if there is a selection.
36116      * @return {Boolean}
36117      */
36118     hasSelection : function(){
36119         return this.selections.length > 0;
36120     },
36121
36122     /**
36123      * Returns True if the specified row is selected.
36124      * @param {Number/Record} record The record or index of the record to check
36125      * @return {Boolean}
36126      */
36127     isSelected : function(index){
36128         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36129         return (r && this.selections.key(r.id) ? true : false);
36130     },
36131
36132     /**
36133      * Returns True if the specified record id is selected.
36134      * @param {String} id The id of record to check
36135      * @return {Boolean}
36136      */
36137     isIdSelected : function(id){
36138         return (this.selections.key(id) ? true : false);
36139     },
36140
36141     // private
36142     handleMouseDown : function(e, t)
36143     {
36144         var view = this.grid.view ? this.grid.view : this.grid;
36145         var rowIndex;
36146         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36147             return;
36148         };
36149         if(e.shiftKey && this.last !== false){
36150             var last = this.last;
36151             this.selectRange(last, rowIndex, e.ctrlKey);
36152             this.last = last; // reset the last
36153             view.focusRow(rowIndex);
36154         }else{
36155             var isSelected = this.isSelected(rowIndex);
36156             if(e.button !== 0 && isSelected){
36157                 view.focusRow(rowIndex);
36158             }else if(e.ctrlKey && isSelected){
36159                 this.deselectRow(rowIndex);
36160             }else if(!isSelected){
36161                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36162                 view.focusRow(rowIndex);
36163             }
36164         }
36165         this.fireEvent("afterselectionchange", this);
36166     },
36167     // private
36168     handleDragableRowClick :  function(grid, rowIndex, e) 
36169     {
36170         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36171             this.selectRow(rowIndex, false);
36172             var view = this.grid.view ? this.grid.view : this.grid;
36173             view.focusRow(rowIndex);
36174              this.fireEvent("afterselectionchange", this);
36175         }
36176     },
36177     
36178     /**
36179      * Selects multiple rows.
36180      * @param {Array} rows Array of the indexes of the row to select
36181      * @param {Boolean} keepExisting (optional) True to keep existing selections
36182      */
36183     selectRows : function(rows, keepExisting){
36184         if(!keepExisting){
36185             this.clearSelections();
36186         }
36187         for(var i = 0, len = rows.length; i < len; i++){
36188             this.selectRow(rows[i], true);
36189         }
36190     },
36191
36192     /**
36193      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36194      * @param {Number} startRow The index of the first row in the range
36195      * @param {Number} endRow The index of the last row in the range
36196      * @param {Boolean} keepExisting (optional) True to retain existing selections
36197      */
36198     selectRange : function(startRow, endRow, keepExisting){
36199         if(this.locked) {
36200             return;
36201         }
36202         if(!keepExisting){
36203             this.clearSelections();
36204         }
36205         if(startRow <= endRow){
36206             for(var i = startRow; i <= endRow; i++){
36207                 this.selectRow(i, true);
36208             }
36209         }else{
36210             for(var i = startRow; i >= endRow; i--){
36211                 this.selectRow(i, true);
36212             }
36213         }
36214     },
36215
36216     /**
36217      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36218      * @param {Number} startRow The index of the first row in the range
36219      * @param {Number} endRow The index of the last row in the range
36220      */
36221     deselectRange : function(startRow, endRow, preventViewNotify){
36222         if(this.locked) {
36223             return;
36224         }
36225         for(var i = startRow; i <= endRow; i++){
36226             this.deselectRow(i, preventViewNotify);
36227         }
36228     },
36229
36230     /**
36231      * Selects a row.
36232      * @param {Number} row The index of the row to select
36233      * @param {Boolean} keepExisting (optional) True to keep existing selections
36234      */
36235     selectRow : function(index, keepExisting, preventViewNotify){
36236         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36237             return;
36238         }
36239         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36240             if(!keepExisting || this.singleSelect){
36241                 this.clearSelections();
36242             }
36243             var r = this.grid.ds.getAt(index);
36244             this.selections.add(r);
36245             this.last = this.lastActive = index;
36246             if(!preventViewNotify){
36247                 var view = this.grid.view ? this.grid.view : this.grid;
36248                 view.onRowSelect(index);
36249             }
36250             this.fireEvent("rowselect", this, index, r);
36251             this.fireEvent("selectionchange", this);
36252         }
36253     },
36254
36255     /**
36256      * Deselects a row.
36257      * @param {Number} row The index of the row to deselect
36258      */
36259     deselectRow : function(index, preventViewNotify){
36260         if(this.locked) {
36261             return;
36262         }
36263         if(this.last == index){
36264             this.last = false;
36265         }
36266         if(this.lastActive == index){
36267             this.lastActive = false;
36268         }
36269         var r = this.grid.ds.getAt(index);
36270         this.selections.remove(r);
36271         if(!preventViewNotify){
36272             var view = this.grid.view ? this.grid.view : this.grid;
36273             view.onRowDeselect(index);
36274         }
36275         this.fireEvent("rowdeselect", this, index);
36276         this.fireEvent("selectionchange", this);
36277     },
36278
36279     // private
36280     restoreLast : function(){
36281         if(this._last){
36282             this.last = this._last;
36283         }
36284     },
36285
36286     // private
36287     acceptsNav : function(row, col, cm){
36288         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36289     },
36290
36291     // private
36292     onEditorKey : function(field, e){
36293         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36294         if(k == e.TAB){
36295             e.stopEvent();
36296             ed.completeEdit();
36297             if(e.shiftKey){
36298                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36299             }else{
36300                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36301             }
36302         }else if(k == e.ENTER && !e.ctrlKey){
36303             e.stopEvent();
36304             ed.completeEdit();
36305             if(e.shiftKey){
36306                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36307             }else{
36308                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36309             }
36310         }else if(k == e.ESC){
36311             ed.cancelEdit();
36312         }
36313         if(newCell){
36314             g.startEditing(newCell[0], newCell[1]);
36315         }
36316     }
36317 });/*
36318  * Based on:
36319  * Ext JS Library 1.1.1
36320  * Copyright(c) 2006-2007, Ext JS, LLC.
36321  *
36322  * Originally Released Under LGPL - original licence link has changed is not relivant.
36323  *
36324  * Fork - LGPL
36325  * <script type="text/javascript">
36326  */
36327 /**
36328  * @class Roo.grid.CellSelectionModel
36329  * @extends Roo.grid.AbstractSelectionModel
36330  * This class provides the basic implementation for cell selection in a grid.
36331  * @constructor
36332  * @param {Object} config The object containing the configuration of this model.
36333  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36334  */
36335 Roo.grid.CellSelectionModel = function(config){
36336     Roo.apply(this, config);
36337
36338     this.selection = null;
36339
36340     this.addEvents({
36341         /**
36342              * @event beforerowselect
36343              * Fires before a cell is selected.
36344              * @param {SelectionModel} this
36345              * @param {Number} rowIndex The selected row index
36346              * @param {Number} colIndex The selected cell index
36347              */
36348             "beforecellselect" : true,
36349         /**
36350              * @event cellselect
36351              * Fires when a cell is selected.
36352              * @param {SelectionModel} this
36353              * @param {Number} rowIndex The selected row index
36354              * @param {Number} colIndex The selected cell index
36355              */
36356             "cellselect" : true,
36357         /**
36358              * @event selectionchange
36359              * Fires when the active selection changes.
36360              * @param {SelectionModel} this
36361              * @param {Object} selection null for no selection or an object (o) with two properties
36362                 <ul>
36363                 <li>o.record: the record object for the row the selection is in</li>
36364                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36365                 </ul>
36366              */
36367             "selectionchange" : true,
36368         /**
36369              * @event tabend
36370              * Fires when the tab (or enter) was pressed on the last editable cell
36371              * You can use this to trigger add new row.
36372              * @param {SelectionModel} this
36373              */
36374             "tabend" : true,
36375          /**
36376              * @event beforeeditnext
36377              * Fires before the next editable sell is made active
36378              * You can use this to skip to another cell or fire the tabend
36379              *    if you set cell to false
36380              * @param {Object} eventdata object : { cell : [ row, col ] } 
36381              */
36382             "beforeeditnext" : true
36383     });
36384     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36385 };
36386
36387 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36388     
36389     enter_is_tab: false,
36390
36391     /** @ignore */
36392     initEvents : function(){
36393         this.grid.on("mousedown", this.handleMouseDown, this);
36394         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36395         var view = this.grid.view;
36396         view.on("refresh", this.onViewChange, this);
36397         view.on("rowupdated", this.onRowUpdated, this);
36398         view.on("beforerowremoved", this.clearSelections, this);
36399         view.on("beforerowsinserted", this.clearSelections, this);
36400         if(this.grid.isEditor){
36401             this.grid.on("beforeedit", this.beforeEdit,  this);
36402         }
36403     },
36404
36405         //private
36406     beforeEdit : function(e){
36407         this.select(e.row, e.column, false, true, e.record);
36408     },
36409
36410         //private
36411     onRowUpdated : function(v, index, r){
36412         if(this.selection && this.selection.record == r){
36413             v.onCellSelect(index, this.selection.cell[1]);
36414         }
36415     },
36416
36417         //private
36418     onViewChange : function(){
36419         this.clearSelections(true);
36420     },
36421
36422         /**
36423          * Returns the currently selected cell,.
36424          * @return {Array} The selected cell (row, column) or null if none selected.
36425          */
36426     getSelectedCell : function(){
36427         return this.selection ? this.selection.cell : null;
36428     },
36429
36430     /**
36431      * Clears all selections.
36432      * @param {Boolean} true to prevent the gridview from being notified about the change.
36433      */
36434     clearSelections : function(preventNotify){
36435         var s = this.selection;
36436         if(s){
36437             if(preventNotify !== true){
36438                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36439             }
36440             this.selection = null;
36441             this.fireEvent("selectionchange", this, null);
36442         }
36443     },
36444
36445     /**
36446      * Returns true if there is a selection.
36447      * @return {Boolean}
36448      */
36449     hasSelection : function(){
36450         return this.selection ? true : false;
36451     },
36452
36453     /** @ignore */
36454     handleMouseDown : function(e, t){
36455         var v = this.grid.getView();
36456         if(this.isLocked()){
36457             return;
36458         };
36459         var row = v.findRowIndex(t);
36460         var cell = v.findCellIndex(t);
36461         if(row !== false && cell !== false){
36462             this.select(row, cell);
36463         }
36464     },
36465
36466     /**
36467      * Selects a cell.
36468      * @param {Number} rowIndex
36469      * @param {Number} collIndex
36470      */
36471     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36472         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36473             this.clearSelections();
36474             r = r || this.grid.dataSource.getAt(rowIndex);
36475             this.selection = {
36476                 record : r,
36477                 cell : [rowIndex, colIndex]
36478             };
36479             if(!preventViewNotify){
36480                 var v = this.grid.getView();
36481                 v.onCellSelect(rowIndex, colIndex);
36482                 if(preventFocus !== true){
36483                     v.focusCell(rowIndex, colIndex);
36484                 }
36485             }
36486             this.fireEvent("cellselect", this, rowIndex, colIndex);
36487             this.fireEvent("selectionchange", this, this.selection);
36488         }
36489     },
36490
36491         //private
36492     isSelectable : function(rowIndex, colIndex, cm){
36493         return !cm.isHidden(colIndex);
36494     },
36495
36496     /** @ignore */
36497     handleKeyDown : function(e){
36498         //Roo.log('Cell Sel Model handleKeyDown');
36499         if(!e.isNavKeyPress()){
36500             return;
36501         }
36502         var g = this.grid, s = this.selection;
36503         if(!s){
36504             e.stopEvent();
36505             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36506             if(cell){
36507                 this.select(cell[0], cell[1]);
36508             }
36509             return;
36510         }
36511         var sm = this;
36512         var walk = function(row, col, step){
36513             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36514         };
36515         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36516         var newCell;
36517
36518       
36519
36520         switch(k){
36521             case e.TAB:
36522                 // handled by onEditorKey
36523                 if (g.isEditor && g.editing) {
36524                     return;
36525                 }
36526                 if(e.shiftKey) {
36527                     newCell = walk(r, c-1, -1);
36528                 } else {
36529                     newCell = walk(r, c+1, 1);
36530                 }
36531                 break;
36532             
36533             case e.DOWN:
36534                newCell = walk(r+1, c, 1);
36535                 break;
36536             
36537             case e.UP:
36538                 newCell = walk(r-1, c, -1);
36539                 break;
36540             
36541             case e.RIGHT:
36542                 newCell = walk(r, c+1, 1);
36543                 break;
36544             
36545             case e.LEFT:
36546                 newCell = walk(r, c-1, -1);
36547                 break;
36548             
36549             case e.ENTER:
36550                 
36551                 if(g.isEditor && !g.editing){
36552                    g.startEditing(r, c);
36553                    e.stopEvent();
36554                    return;
36555                 }
36556                 
36557                 
36558              break;
36559         };
36560         if(newCell){
36561             this.select(newCell[0], newCell[1]);
36562             e.stopEvent();
36563             
36564         }
36565     },
36566
36567     acceptsNav : function(row, col, cm){
36568         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36569     },
36570     /**
36571      * Selects a cell.
36572      * @param {Number} field (not used) - as it's normally used as a listener
36573      * @param {Number} e - event - fake it by using
36574      *
36575      * var e = Roo.EventObjectImpl.prototype;
36576      * e.keyCode = e.TAB
36577      *
36578      * 
36579      */
36580     onEditorKey : function(field, e){
36581         
36582         var k = e.getKey(),
36583             newCell,
36584             g = this.grid,
36585             ed = g.activeEditor,
36586             forward = false;
36587         ///Roo.log('onEditorKey' + k);
36588         
36589         
36590         if (this.enter_is_tab && k == e.ENTER) {
36591             k = e.TAB;
36592         }
36593         
36594         if(k == e.TAB){
36595             if(e.shiftKey){
36596                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36597             }else{
36598                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36599                 forward = true;
36600             }
36601             
36602             e.stopEvent();
36603             
36604         } else if(k == e.ENTER &&  !e.ctrlKey){
36605             ed.completeEdit();
36606             e.stopEvent();
36607             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36608         
36609                 } else if(k == e.ESC){
36610             ed.cancelEdit();
36611         }
36612                 
36613         if (newCell) {
36614             var ecall = { cell : newCell, forward : forward };
36615             this.fireEvent('beforeeditnext', ecall );
36616             newCell = ecall.cell;
36617                         forward = ecall.forward;
36618         }
36619                 
36620         if(newCell){
36621             //Roo.log('next cell after edit');
36622             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36623         } else if (forward) {
36624             // tabbed past last
36625             this.fireEvent.defer(100, this, ['tabend',this]);
36626         }
36627     }
36628 });/*
36629  * Based on:
36630  * Ext JS Library 1.1.1
36631  * Copyright(c) 2006-2007, Ext JS, LLC.
36632  *
36633  * Originally Released Under LGPL - original licence link has changed is not relivant.
36634  *
36635  * Fork - LGPL
36636  * <script type="text/javascript">
36637  */
36638  
36639 /**
36640  * @class Roo.grid.EditorGrid
36641  * @extends Roo.grid.Grid
36642  * Class for creating and editable grid.
36643  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36644  * The container MUST have some type of size defined for the grid to fill. The container will be 
36645  * automatically set to position relative if it isn't already.
36646  * @param {Object} dataSource The data model to bind to
36647  * @param {Object} colModel The column model with info about this grid's columns
36648  */
36649 Roo.grid.EditorGrid = function(container, config){
36650     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36651     this.getGridEl().addClass("xedit-grid");
36652
36653     if(!this.selModel){
36654         this.selModel = new Roo.grid.CellSelectionModel();
36655     }
36656
36657     this.activeEditor = null;
36658
36659         this.addEvents({
36660             /**
36661              * @event beforeedit
36662              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36663              * <ul style="padding:5px;padding-left:16px;">
36664              * <li>grid - This grid</li>
36665              * <li>record - The record being edited</li>
36666              * <li>field - The field name being edited</li>
36667              * <li>value - The value for the field being edited.</li>
36668              * <li>row - The grid row index</li>
36669              * <li>column - The grid column index</li>
36670              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36671              * </ul>
36672              * @param {Object} e An edit event (see above for description)
36673              */
36674             "beforeedit" : true,
36675             /**
36676              * @event afteredit
36677              * Fires after a cell is edited. <br />
36678              * <ul style="padding:5px;padding-left:16px;">
36679              * <li>grid - This grid</li>
36680              * <li>record - The record being edited</li>
36681              * <li>field - The field name being edited</li>
36682              * <li>value - The value being set</li>
36683              * <li>originalValue - The original value for the field, before the edit.</li>
36684              * <li>row - The grid row index</li>
36685              * <li>column - The grid column index</li>
36686              * </ul>
36687              * @param {Object} e An edit event (see above for description)
36688              */
36689             "afteredit" : true,
36690             /**
36691              * @event validateedit
36692              * Fires after a cell is edited, but before the value is set in the record. 
36693          * You can use this to modify the value being set in the field, Return false
36694              * to cancel the change. The edit event object has the following properties <br />
36695              * <ul style="padding:5px;padding-left:16px;">
36696          * <li>editor - This editor</li>
36697              * <li>grid - This grid</li>
36698              * <li>record - The record being edited</li>
36699              * <li>field - The field name being edited</li>
36700              * <li>value - The value being set</li>
36701              * <li>originalValue - The original value for the field, before the edit.</li>
36702              * <li>row - The grid row index</li>
36703              * <li>column - The grid column index</li>
36704              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36705              * </ul>
36706              * @param {Object} e An edit event (see above for description)
36707              */
36708             "validateedit" : true
36709         });
36710     this.on("bodyscroll", this.stopEditing,  this);
36711     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36712 };
36713
36714 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36715     /**
36716      * @cfg {Number} clicksToEdit
36717      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36718      */
36719     clicksToEdit: 2,
36720
36721     // private
36722     isEditor : true,
36723     // private
36724     trackMouseOver: false, // causes very odd FF errors
36725
36726     onCellDblClick : function(g, row, col){
36727         this.startEditing(row, col);
36728     },
36729
36730     onEditComplete : function(ed, value, startValue){
36731         this.editing = false;
36732         this.activeEditor = null;
36733         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36734         var r = ed.record;
36735         var field = this.colModel.getDataIndex(ed.col);
36736         var e = {
36737             grid: this,
36738             record: r,
36739             field: field,
36740             originalValue: startValue,
36741             value: value,
36742             row: ed.row,
36743             column: ed.col,
36744             cancel:false,
36745             editor: ed
36746         };
36747         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36748         cell.show();
36749           
36750         if(String(value) !== String(startValue)){
36751             
36752             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36753                 r.set(field, e.value);
36754                 // if we are dealing with a combo box..
36755                 // then we also set the 'name' colum to be the displayField
36756                 if (ed.field.displayField && ed.field.name) {
36757                     r.set(ed.field.name, ed.field.el.dom.value);
36758                 }
36759                 
36760                 delete e.cancel; //?? why!!!
36761                 this.fireEvent("afteredit", e);
36762             }
36763         } else {
36764             this.fireEvent("afteredit", e); // always fire it!
36765         }
36766         this.view.focusCell(ed.row, ed.col);
36767     },
36768
36769     /**
36770      * Starts editing the specified for the specified row/column
36771      * @param {Number} rowIndex
36772      * @param {Number} colIndex
36773      */
36774     startEditing : function(row, col){
36775         this.stopEditing();
36776         if(this.colModel.isCellEditable(col, row)){
36777             this.view.ensureVisible(row, col, true);
36778           
36779             var r = this.dataSource.getAt(row);
36780             var field = this.colModel.getDataIndex(col);
36781             var cell = Roo.get(this.view.getCell(row,col));
36782             var e = {
36783                 grid: this,
36784                 record: r,
36785                 field: field,
36786                 value: r.data[field],
36787                 row: row,
36788                 column: col,
36789                 cancel:false 
36790             };
36791             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36792                 this.editing = true;
36793                 var ed = this.colModel.getCellEditor(col, row);
36794                 
36795                 if (!ed) {
36796                     return;
36797                 }
36798                 if(!ed.rendered){
36799                     ed.render(ed.parentEl || document.body);
36800                 }
36801                 ed.field.reset();
36802                
36803                 cell.hide();
36804                 
36805                 (function(){ // complex but required for focus issues in safari, ie and opera
36806                     ed.row = row;
36807                     ed.col = col;
36808                     ed.record = r;
36809                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36810                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36811                     this.activeEditor = ed;
36812                     var v = r.data[field];
36813                     ed.startEdit(this.view.getCell(row, col), v);
36814                     // combo's with 'displayField and name set
36815                     if (ed.field.displayField && ed.field.name) {
36816                         ed.field.el.dom.value = r.data[ed.field.name];
36817                     }
36818                     
36819                     
36820                 }).defer(50, this);
36821             }
36822         }
36823     },
36824         
36825     /**
36826      * Stops any active editing
36827      */
36828     stopEditing : function(){
36829         if(this.activeEditor){
36830             this.activeEditor.completeEdit();
36831         }
36832         this.activeEditor = null;
36833     },
36834         
36835          /**
36836      * Called to get grid's drag proxy text, by default returns this.ddText.
36837      * @return {String}
36838      */
36839     getDragDropText : function(){
36840         var count = this.selModel.getSelectedCell() ? 1 : 0;
36841         return String.format(this.ddText, count, count == 1 ? '' : 's');
36842     }
36843         
36844 });/*
36845  * Based on:
36846  * Ext JS Library 1.1.1
36847  * Copyright(c) 2006-2007, Ext JS, LLC.
36848  *
36849  * Originally Released Under LGPL - original licence link has changed is not relivant.
36850  *
36851  * Fork - LGPL
36852  * <script type="text/javascript">
36853  */
36854
36855 // private - not really -- you end up using it !
36856 // This is a support class used internally by the Grid components
36857
36858 /**
36859  * @class Roo.grid.GridEditor
36860  * @extends Roo.Editor
36861  * Class for creating and editable grid elements.
36862  * @param {Object} config any settings (must include field)
36863  */
36864 Roo.grid.GridEditor = function(field, config){
36865     if (!config && field.field) {
36866         config = field;
36867         field = Roo.factory(config.field, Roo.form);
36868     }
36869     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36870     field.monitorTab = false;
36871 };
36872
36873 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36874     
36875     /**
36876      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36877      */
36878     
36879     alignment: "tl-tl",
36880     autoSize: "width",
36881     hideEl : false,
36882     cls: "x-small-editor x-grid-editor",
36883     shim:false,
36884     shadow:"frame"
36885 });/*
36886  * Based on:
36887  * Ext JS Library 1.1.1
36888  * Copyright(c) 2006-2007, Ext JS, LLC.
36889  *
36890  * Originally Released Under LGPL - original licence link has changed is not relivant.
36891  *
36892  * Fork - LGPL
36893  * <script type="text/javascript">
36894  */
36895   
36896
36897   
36898 Roo.grid.PropertyRecord = Roo.data.Record.create([
36899     {name:'name',type:'string'},  'value'
36900 ]);
36901
36902
36903 Roo.grid.PropertyStore = function(grid, source){
36904     this.grid = grid;
36905     this.store = new Roo.data.Store({
36906         recordType : Roo.grid.PropertyRecord
36907     });
36908     this.store.on('update', this.onUpdate,  this);
36909     if(source){
36910         this.setSource(source);
36911     }
36912     Roo.grid.PropertyStore.superclass.constructor.call(this);
36913 };
36914
36915
36916
36917 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36918     setSource : function(o){
36919         this.source = o;
36920         this.store.removeAll();
36921         var data = [];
36922         for(var k in o){
36923             if(this.isEditableValue(o[k])){
36924                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36925             }
36926         }
36927         this.store.loadRecords({records: data}, {}, true);
36928     },
36929
36930     onUpdate : function(ds, record, type){
36931         if(type == Roo.data.Record.EDIT){
36932             var v = record.data['value'];
36933             var oldValue = record.modified['value'];
36934             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36935                 this.source[record.id] = v;
36936                 record.commit();
36937                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36938             }else{
36939                 record.reject();
36940             }
36941         }
36942     },
36943
36944     getProperty : function(row){
36945        return this.store.getAt(row);
36946     },
36947
36948     isEditableValue: function(val){
36949         if(val && val instanceof Date){
36950             return true;
36951         }else if(typeof val == 'object' || typeof val == 'function'){
36952             return false;
36953         }
36954         return true;
36955     },
36956
36957     setValue : function(prop, value){
36958         this.source[prop] = value;
36959         this.store.getById(prop).set('value', value);
36960     },
36961
36962     getSource : function(){
36963         return this.source;
36964     }
36965 });
36966
36967 Roo.grid.PropertyColumnModel = function(grid, store){
36968     this.grid = grid;
36969     var g = Roo.grid;
36970     g.PropertyColumnModel.superclass.constructor.call(this, [
36971         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36972         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36973     ]);
36974     this.store = store;
36975     this.bselect = Roo.DomHelper.append(document.body, {
36976         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36977             {tag: 'option', value: 'true', html: 'true'},
36978             {tag: 'option', value: 'false', html: 'false'}
36979         ]
36980     });
36981     Roo.id(this.bselect);
36982     var f = Roo.form;
36983     this.editors = {
36984         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36985         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36986         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36987         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36988         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36989     };
36990     this.renderCellDelegate = this.renderCell.createDelegate(this);
36991     this.renderPropDelegate = this.renderProp.createDelegate(this);
36992 };
36993
36994 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36995     
36996     
36997     nameText : 'Name',
36998     valueText : 'Value',
36999     
37000     dateFormat : 'm/j/Y',
37001     
37002     
37003     renderDate : function(dateVal){
37004         return dateVal.dateFormat(this.dateFormat);
37005     },
37006
37007     renderBool : function(bVal){
37008         return bVal ? 'true' : 'false';
37009     },
37010
37011     isCellEditable : function(colIndex, rowIndex){
37012         return colIndex == 1;
37013     },
37014
37015     getRenderer : function(col){
37016         return col == 1 ?
37017             this.renderCellDelegate : this.renderPropDelegate;
37018     },
37019
37020     renderProp : function(v){
37021         return this.getPropertyName(v);
37022     },
37023
37024     renderCell : function(val){
37025         var rv = val;
37026         if(val instanceof Date){
37027             rv = this.renderDate(val);
37028         }else if(typeof val == 'boolean'){
37029             rv = this.renderBool(val);
37030         }
37031         return Roo.util.Format.htmlEncode(rv);
37032     },
37033
37034     getPropertyName : function(name){
37035         var pn = this.grid.propertyNames;
37036         return pn && pn[name] ? pn[name] : name;
37037     },
37038
37039     getCellEditor : function(colIndex, rowIndex){
37040         var p = this.store.getProperty(rowIndex);
37041         var n = p.data['name'], val = p.data['value'];
37042         
37043         if(typeof(this.grid.customEditors[n]) == 'string'){
37044             return this.editors[this.grid.customEditors[n]];
37045         }
37046         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37047             return this.grid.customEditors[n];
37048         }
37049         if(val instanceof Date){
37050             return this.editors['date'];
37051         }else if(typeof val == 'number'){
37052             return this.editors['number'];
37053         }else if(typeof val == 'boolean'){
37054             return this.editors['boolean'];
37055         }else{
37056             return this.editors['string'];
37057         }
37058     }
37059 });
37060
37061 /**
37062  * @class Roo.grid.PropertyGrid
37063  * @extends Roo.grid.EditorGrid
37064  * This class represents the  interface of a component based property grid control.
37065  * <br><br>Usage:<pre><code>
37066  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37067       
37068  });
37069  // set any options
37070  grid.render();
37071  * </code></pre>
37072   
37073  * @constructor
37074  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37075  * The container MUST have some type of size defined for the grid to fill. The container will be
37076  * automatically set to position relative if it isn't already.
37077  * @param {Object} config A config object that sets properties on this grid.
37078  */
37079 Roo.grid.PropertyGrid = function(container, config){
37080     config = config || {};
37081     var store = new Roo.grid.PropertyStore(this);
37082     this.store = store;
37083     var cm = new Roo.grid.PropertyColumnModel(this, store);
37084     store.store.sort('name', 'ASC');
37085     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37086         ds: store.store,
37087         cm: cm,
37088         enableColLock:false,
37089         enableColumnMove:false,
37090         stripeRows:false,
37091         trackMouseOver: false,
37092         clicksToEdit:1
37093     }, config));
37094     this.getGridEl().addClass('x-props-grid');
37095     this.lastEditRow = null;
37096     this.on('columnresize', this.onColumnResize, this);
37097     this.addEvents({
37098          /**
37099              * @event beforepropertychange
37100              * Fires before a property changes (return false to stop?)
37101              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37102              * @param {String} id Record Id
37103              * @param {String} newval New Value
37104          * @param {String} oldval Old Value
37105              */
37106         "beforepropertychange": true,
37107         /**
37108              * @event propertychange
37109              * Fires after a property changes
37110              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37111              * @param {String} id Record Id
37112              * @param {String} newval New Value
37113          * @param {String} oldval Old Value
37114              */
37115         "propertychange": true
37116     });
37117     this.customEditors = this.customEditors || {};
37118 };
37119 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37120     
37121      /**
37122      * @cfg {Object} customEditors map of colnames=> custom editors.
37123      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37124      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37125      * false disables editing of the field.
37126          */
37127     
37128       /**
37129      * @cfg {Object} propertyNames map of property Names to their displayed value
37130          */
37131     
37132     render : function(){
37133         Roo.grid.PropertyGrid.superclass.render.call(this);
37134         this.autoSize.defer(100, this);
37135     },
37136
37137     autoSize : function(){
37138         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37139         if(this.view){
37140             this.view.fitColumns();
37141         }
37142     },
37143
37144     onColumnResize : function(){
37145         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37146         this.autoSize();
37147     },
37148     /**
37149      * Sets the data for the Grid
37150      * accepts a Key => Value object of all the elements avaiable.
37151      * @param {Object} data  to appear in grid.
37152      */
37153     setSource : function(source){
37154         this.store.setSource(source);
37155         //this.autoSize();
37156     },
37157     /**
37158      * Gets all the data from the grid.
37159      * @return {Object} data  data stored in grid
37160      */
37161     getSource : function(){
37162         return this.store.getSource();
37163     }
37164 });/*
37165   
37166  * Licence LGPL
37167  
37168  */
37169  
37170 /**
37171  * @class Roo.grid.Calendar
37172  * @extends Roo.util.Grid
37173  * This class extends the Grid to provide a calendar widget
37174  * <br><br>Usage:<pre><code>
37175  var grid = new Roo.grid.Calendar("my-container-id", {
37176      ds: myDataStore,
37177      cm: myColModel,
37178      selModel: mySelectionModel,
37179      autoSizeColumns: true,
37180      monitorWindowResize: false,
37181      trackMouseOver: true
37182      eventstore : real data store..
37183  });
37184  // set any options
37185  grid.render();
37186   
37187   * @constructor
37188  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37189  * The container MUST have some type of size defined for the grid to fill. The container will be
37190  * automatically set to position relative if it isn't already.
37191  * @param {Object} config A config object that sets properties on this grid.
37192  */
37193 Roo.grid.Calendar = function(container, config){
37194         // initialize the container
37195         this.container = Roo.get(container);
37196         this.container.update("");
37197         this.container.setStyle("overflow", "hidden");
37198     this.container.addClass('x-grid-container');
37199
37200     this.id = this.container.id;
37201
37202     Roo.apply(this, config);
37203     // check and correct shorthanded configs
37204     
37205     var rows = [];
37206     var d =1;
37207     for (var r = 0;r < 6;r++) {
37208         
37209         rows[r]=[];
37210         for (var c =0;c < 7;c++) {
37211             rows[r][c]= '';
37212         }
37213     }
37214     if (this.eventStore) {
37215         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37216         this.eventStore.on('load',this.onLoad, this);
37217         this.eventStore.on('beforeload',this.clearEvents, this);
37218          
37219     }
37220     
37221     this.dataSource = new Roo.data.Store({
37222             proxy: new Roo.data.MemoryProxy(rows),
37223             reader: new Roo.data.ArrayReader({}, [
37224                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37225     });
37226
37227     this.dataSource.load();
37228     this.ds = this.dataSource;
37229     this.ds.xmodule = this.xmodule || false;
37230     
37231     
37232     var cellRender = function(v,x,r)
37233     {
37234         return String.format(
37235             '<div class="fc-day  fc-widget-content"><div>' +
37236                 '<div class="fc-event-container"></div>' +
37237                 '<div class="fc-day-number">{0}</div>'+
37238                 
37239                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37240             '</div></div>', v);
37241     
37242     }
37243     
37244     
37245     this.colModel = new Roo.grid.ColumnModel( [
37246         {
37247             xtype: 'ColumnModel',
37248             xns: Roo.grid,
37249             dataIndex : 'weekday0',
37250             header : 'Sunday',
37251             renderer : cellRender
37252         },
37253         {
37254             xtype: 'ColumnModel',
37255             xns: Roo.grid,
37256             dataIndex : 'weekday1',
37257             header : 'Monday',
37258             renderer : cellRender
37259         },
37260         {
37261             xtype: 'ColumnModel',
37262             xns: Roo.grid,
37263             dataIndex : 'weekday2',
37264             header : 'Tuesday',
37265             renderer : cellRender
37266         },
37267         {
37268             xtype: 'ColumnModel',
37269             xns: Roo.grid,
37270             dataIndex : 'weekday3',
37271             header : 'Wednesday',
37272             renderer : cellRender
37273         },
37274         {
37275             xtype: 'ColumnModel',
37276             xns: Roo.grid,
37277             dataIndex : 'weekday4',
37278             header : 'Thursday',
37279             renderer : cellRender
37280         },
37281         {
37282             xtype: 'ColumnModel',
37283             xns: Roo.grid,
37284             dataIndex : 'weekday5',
37285             header : 'Friday',
37286             renderer : cellRender
37287         },
37288         {
37289             xtype: 'ColumnModel',
37290             xns: Roo.grid,
37291             dataIndex : 'weekday6',
37292             header : 'Saturday',
37293             renderer : cellRender
37294         }
37295     ]);
37296     this.cm = this.colModel;
37297     this.cm.xmodule = this.xmodule || false;
37298  
37299         
37300           
37301     //this.selModel = new Roo.grid.CellSelectionModel();
37302     //this.sm = this.selModel;
37303     //this.selModel.init(this);
37304     
37305     
37306     if(this.width){
37307         this.container.setWidth(this.width);
37308     }
37309
37310     if(this.height){
37311         this.container.setHeight(this.height);
37312     }
37313     /** @private */
37314         this.addEvents({
37315         // raw events
37316         /**
37317          * @event click
37318          * The raw click event for the entire grid.
37319          * @param {Roo.EventObject} e
37320          */
37321         "click" : true,
37322         /**
37323          * @event dblclick
37324          * The raw dblclick event for the entire grid.
37325          * @param {Roo.EventObject} e
37326          */
37327         "dblclick" : true,
37328         /**
37329          * @event contextmenu
37330          * The raw contextmenu event for the entire grid.
37331          * @param {Roo.EventObject} e
37332          */
37333         "contextmenu" : true,
37334         /**
37335          * @event mousedown
37336          * The raw mousedown event for the entire grid.
37337          * @param {Roo.EventObject} e
37338          */
37339         "mousedown" : true,
37340         /**
37341          * @event mouseup
37342          * The raw mouseup event for the entire grid.
37343          * @param {Roo.EventObject} e
37344          */
37345         "mouseup" : true,
37346         /**
37347          * @event mouseover
37348          * The raw mouseover event for the entire grid.
37349          * @param {Roo.EventObject} e
37350          */
37351         "mouseover" : true,
37352         /**
37353          * @event mouseout
37354          * The raw mouseout event for the entire grid.
37355          * @param {Roo.EventObject} e
37356          */
37357         "mouseout" : true,
37358         /**
37359          * @event keypress
37360          * The raw keypress event for the entire grid.
37361          * @param {Roo.EventObject} e
37362          */
37363         "keypress" : true,
37364         /**
37365          * @event keydown
37366          * The raw keydown event for the entire grid.
37367          * @param {Roo.EventObject} e
37368          */
37369         "keydown" : true,
37370
37371         // custom events
37372
37373         /**
37374          * @event cellclick
37375          * Fires when a cell is clicked
37376          * @param {Grid} this
37377          * @param {Number} rowIndex
37378          * @param {Number} columnIndex
37379          * @param {Roo.EventObject} e
37380          */
37381         "cellclick" : true,
37382         /**
37383          * @event celldblclick
37384          * Fires when a cell is double clicked
37385          * @param {Grid} this
37386          * @param {Number} rowIndex
37387          * @param {Number} columnIndex
37388          * @param {Roo.EventObject} e
37389          */
37390         "celldblclick" : true,
37391         /**
37392          * @event rowclick
37393          * Fires when a row is clicked
37394          * @param {Grid} this
37395          * @param {Number} rowIndex
37396          * @param {Roo.EventObject} e
37397          */
37398         "rowclick" : true,
37399         /**
37400          * @event rowdblclick
37401          * Fires when a row is double clicked
37402          * @param {Grid} this
37403          * @param {Number} rowIndex
37404          * @param {Roo.EventObject} e
37405          */
37406         "rowdblclick" : true,
37407         /**
37408          * @event headerclick
37409          * Fires when a header is clicked
37410          * @param {Grid} this
37411          * @param {Number} columnIndex
37412          * @param {Roo.EventObject} e
37413          */
37414         "headerclick" : true,
37415         /**
37416          * @event headerdblclick
37417          * Fires when a header cell is double clicked
37418          * @param {Grid} this
37419          * @param {Number} columnIndex
37420          * @param {Roo.EventObject} e
37421          */
37422         "headerdblclick" : true,
37423         /**
37424          * @event rowcontextmenu
37425          * Fires when a row is right clicked
37426          * @param {Grid} this
37427          * @param {Number} rowIndex
37428          * @param {Roo.EventObject} e
37429          */
37430         "rowcontextmenu" : true,
37431         /**
37432          * @event cellcontextmenu
37433          * Fires when a cell is right clicked
37434          * @param {Grid} this
37435          * @param {Number} rowIndex
37436          * @param {Number} cellIndex
37437          * @param {Roo.EventObject} e
37438          */
37439          "cellcontextmenu" : true,
37440         /**
37441          * @event headercontextmenu
37442          * Fires when a header is right clicked
37443          * @param {Grid} this
37444          * @param {Number} columnIndex
37445          * @param {Roo.EventObject} e
37446          */
37447         "headercontextmenu" : true,
37448         /**
37449          * @event bodyscroll
37450          * Fires when the body element is scrolled
37451          * @param {Number} scrollLeft
37452          * @param {Number} scrollTop
37453          */
37454         "bodyscroll" : true,
37455         /**
37456          * @event columnresize
37457          * Fires when the user resizes a column
37458          * @param {Number} columnIndex
37459          * @param {Number} newSize
37460          */
37461         "columnresize" : true,
37462         /**
37463          * @event columnmove
37464          * Fires when the user moves a column
37465          * @param {Number} oldIndex
37466          * @param {Number} newIndex
37467          */
37468         "columnmove" : true,
37469         /**
37470          * @event startdrag
37471          * Fires when row(s) start being dragged
37472          * @param {Grid} this
37473          * @param {Roo.GridDD} dd The drag drop object
37474          * @param {event} e The raw browser event
37475          */
37476         "startdrag" : true,
37477         /**
37478          * @event enddrag
37479          * Fires when a drag operation is complete
37480          * @param {Grid} this
37481          * @param {Roo.GridDD} dd The drag drop object
37482          * @param {event} e The raw browser event
37483          */
37484         "enddrag" : true,
37485         /**
37486          * @event dragdrop
37487          * Fires when dragged row(s) are dropped on a valid DD target
37488          * @param {Grid} this
37489          * @param {Roo.GridDD} dd The drag drop object
37490          * @param {String} targetId The target drag drop object
37491          * @param {event} e The raw browser event
37492          */
37493         "dragdrop" : true,
37494         /**
37495          * @event dragover
37496          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37497          * @param {Grid} this
37498          * @param {Roo.GridDD} dd The drag drop object
37499          * @param {String} targetId The target drag drop object
37500          * @param {event} e The raw browser event
37501          */
37502         "dragover" : true,
37503         /**
37504          * @event dragenter
37505          *  Fires when the dragged row(s) first cross another DD target while being dragged
37506          * @param {Grid} this
37507          * @param {Roo.GridDD} dd The drag drop object
37508          * @param {String} targetId The target drag drop object
37509          * @param {event} e The raw browser event
37510          */
37511         "dragenter" : true,
37512         /**
37513          * @event dragout
37514          * Fires when the dragged row(s) leave another DD target while being dragged
37515          * @param {Grid} this
37516          * @param {Roo.GridDD} dd The drag drop object
37517          * @param {String} targetId The target drag drop object
37518          * @param {event} e The raw browser event
37519          */
37520         "dragout" : true,
37521         /**
37522          * @event rowclass
37523          * Fires when a row is rendered, so you can change add a style to it.
37524          * @param {GridView} gridview   The grid view
37525          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37526          */
37527         'rowclass' : true,
37528
37529         /**
37530          * @event render
37531          * Fires when the grid is rendered
37532          * @param {Grid} grid
37533          */
37534         'render' : true,
37535             /**
37536              * @event select
37537              * Fires when a date is selected
37538              * @param {DatePicker} this
37539              * @param {Date} date The selected date
37540              */
37541         'select': true,
37542         /**
37543              * @event monthchange
37544              * Fires when the displayed month changes 
37545              * @param {DatePicker} this
37546              * @param {Date} date The selected month
37547              */
37548         'monthchange': true,
37549         /**
37550              * @event evententer
37551              * Fires when mouse over an event
37552              * @param {Calendar} this
37553              * @param {event} Event
37554              */
37555         'evententer': true,
37556         /**
37557              * @event eventleave
37558              * Fires when the mouse leaves an
37559              * @param {Calendar} this
37560              * @param {event}
37561              */
37562         'eventleave': true,
37563         /**
37564              * @event eventclick
37565              * Fires when the mouse click an
37566              * @param {Calendar} this
37567              * @param {event}
37568              */
37569         'eventclick': true,
37570         /**
37571              * @event eventrender
37572              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37573              * @param {Calendar} this
37574              * @param {data} data to be modified
37575              */
37576         'eventrender': true
37577         
37578     });
37579
37580     Roo.grid.Grid.superclass.constructor.call(this);
37581     this.on('render', function() {
37582         this.view.el.addClass('x-grid-cal'); 
37583         
37584         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37585
37586     },this);
37587     
37588     if (!Roo.grid.Calendar.style) {
37589         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37590             
37591             
37592             '.x-grid-cal .x-grid-col' :  {
37593                 height: 'auto !important',
37594                 'vertical-align': 'top'
37595             },
37596             '.x-grid-cal  .fc-event-hori' : {
37597                 height: '14px'
37598             }
37599              
37600             
37601         }, Roo.id());
37602     }
37603
37604     
37605     
37606 };
37607 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37608     /**
37609      * @cfg {Store} eventStore The store that loads events.
37610      */
37611     eventStore : 25,
37612
37613      
37614     activeDate : false,
37615     startDay : 0,
37616     autoWidth : true,
37617     monitorWindowResize : false,
37618
37619     
37620     resizeColumns : function() {
37621         var col = (this.view.el.getWidth() / 7) - 3;
37622         // loop through cols, and setWidth
37623         for(var i =0 ; i < 7 ; i++){
37624             this.cm.setColumnWidth(i, col);
37625         }
37626     },
37627      setDate :function(date) {
37628         
37629         Roo.log('setDate?');
37630         
37631         this.resizeColumns();
37632         var vd = this.activeDate;
37633         this.activeDate = date;
37634 //        if(vd && this.el){
37635 //            var t = date.getTime();
37636 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37637 //                Roo.log('using add remove');
37638 //                
37639 //                this.fireEvent('monthchange', this, date);
37640 //                
37641 //                this.cells.removeClass("fc-state-highlight");
37642 //                this.cells.each(function(c){
37643 //                   if(c.dateValue == t){
37644 //                       c.addClass("fc-state-highlight");
37645 //                       setTimeout(function(){
37646 //                            try{c.dom.firstChild.focus();}catch(e){}
37647 //                       }, 50);
37648 //                       return false;
37649 //                   }
37650 //                   return true;
37651 //                });
37652 //                return;
37653 //            }
37654 //        }
37655         
37656         var days = date.getDaysInMonth();
37657         
37658         var firstOfMonth = date.getFirstDateOfMonth();
37659         var startingPos = firstOfMonth.getDay()-this.startDay;
37660         
37661         if(startingPos < this.startDay){
37662             startingPos += 7;
37663         }
37664         
37665         var pm = date.add(Date.MONTH, -1);
37666         var prevStart = pm.getDaysInMonth()-startingPos;
37667 //        
37668         
37669         
37670         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37671         
37672         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37673         //this.cells.addClassOnOver('fc-state-hover');
37674         
37675         var cells = this.cells.elements;
37676         var textEls = this.textNodes;
37677         
37678         //Roo.each(cells, function(cell){
37679         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37680         //});
37681         
37682         days += startingPos;
37683
37684         // convert everything to numbers so it's fast
37685         var day = 86400000;
37686         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37687         //Roo.log(d);
37688         //Roo.log(pm);
37689         //Roo.log(prevStart);
37690         
37691         var today = new Date().clearTime().getTime();
37692         var sel = date.clearTime().getTime();
37693         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37694         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37695         var ddMatch = this.disabledDatesRE;
37696         var ddText = this.disabledDatesText;
37697         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37698         var ddaysText = this.disabledDaysText;
37699         var format = this.format;
37700         
37701         var setCellClass = function(cal, cell){
37702             
37703             //Roo.log('set Cell Class');
37704             cell.title = "";
37705             var t = d.getTime();
37706             
37707             //Roo.log(d);
37708             
37709             
37710             cell.dateValue = t;
37711             if(t == today){
37712                 cell.className += " fc-today";
37713                 cell.className += " fc-state-highlight";
37714                 cell.title = cal.todayText;
37715             }
37716             if(t == sel){
37717                 // disable highlight in other month..
37718                 cell.className += " fc-state-highlight";
37719                 
37720             }
37721             // disabling
37722             if(t < min) {
37723                 //cell.className = " fc-state-disabled";
37724                 cell.title = cal.minText;
37725                 return;
37726             }
37727             if(t > max) {
37728                 //cell.className = " fc-state-disabled";
37729                 cell.title = cal.maxText;
37730                 return;
37731             }
37732             if(ddays){
37733                 if(ddays.indexOf(d.getDay()) != -1){
37734                     // cell.title = ddaysText;
37735                    // cell.className = " fc-state-disabled";
37736                 }
37737             }
37738             if(ddMatch && format){
37739                 var fvalue = d.dateFormat(format);
37740                 if(ddMatch.test(fvalue)){
37741                     cell.title = ddText.replace("%0", fvalue);
37742                    cell.className = " fc-state-disabled";
37743                 }
37744             }
37745             
37746             if (!cell.initialClassName) {
37747                 cell.initialClassName = cell.dom.className;
37748             }
37749             
37750             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37751         };
37752
37753         var i = 0;
37754         
37755         for(; i < startingPos; i++) {
37756             cells[i].dayName =  (++prevStart);
37757             Roo.log(textEls[i]);
37758             d.setDate(d.getDate()+1);
37759             
37760             //cells[i].className = "fc-past fc-other-month";
37761             setCellClass(this, cells[i]);
37762         }
37763         
37764         var intDay = 0;
37765         
37766         for(; i < days; i++){
37767             intDay = i - startingPos + 1;
37768             cells[i].dayName =  (intDay);
37769             d.setDate(d.getDate()+1);
37770             
37771             cells[i].className = ''; // "x-date-active";
37772             setCellClass(this, cells[i]);
37773         }
37774         var extraDays = 0;
37775         
37776         for(; i < 42; i++) {
37777             //textEls[i].innerHTML = (++extraDays);
37778             
37779             d.setDate(d.getDate()+1);
37780             cells[i].dayName = (++extraDays);
37781             cells[i].className = "fc-future fc-other-month";
37782             setCellClass(this, cells[i]);
37783         }
37784         
37785         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37786         
37787         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37788         
37789         // this will cause all the cells to mis
37790         var rows= [];
37791         var i =0;
37792         for (var r = 0;r < 6;r++) {
37793             for (var c =0;c < 7;c++) {
37794                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37795             }    
37796         }
37797         
37798         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37799         for(i=0;i<cells.length;i++) {
37800             
37801             this.cells.elements[i].dayName = cells[i].dayName ;
37802             this.cells.elements[i].className = cells[i].className;
37803             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37804             this.cells.elements[i].title = cells[i].title ;
37805             this.cells.elements[i].dateValue = cells[i].dateValue ;
37806         }
37807         
37808         
37809         
37810         
37811         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37812         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37813         
37814         ////if(totalRows != 6){
37815             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37816            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37817        // }
37818         
37819         this.fireEvent('monthchange', this, date);
37820         
37821         
37822     },
37823  /**
37824      * Returns the grid's SelectionModel.
37825      * @return {SelectionModel}
37826      */
37827     getSelectionModel : function(){
37828         if(!this.selModel){
37829             this.selModel = new Roo.grid.CellSelectionModel();
37830         }
37831         return this.selModel;
37832     },
37833
37834     load: function() {
37835         this.eventStore.load()
37836         
37837         
37838         
37839     },
37840     
37841     findCell : function(dt) {
37842         dt = dt.clearTime().getTime();
37843         var ret = false;
37844         this.cells.each(function(c){
37845             //Roo.log("check " +c.dateValue + '?=' + dt);
37846             if(c.dateValue == dt){
37847                 ret = c;
37848                 return false;
37849             }
37850             return true;
37851         });
37852         
37853         return ret;
37854     },
37855     
37856     findCells : function(rec) {
37857         var s = rec.data.start_dt.clone().clearTime().getTime();
37858        // Roo.log(s);
37859         var e= rec.data.end_dt.clone().clearTime().getTime();
37860        // Roo.log(e);
37861         var ret = [];
37862         this.cells.each(function(c){
37863              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37864             
37865             if(c.dateValue > e){
37866                 return ;
37867             }
37868             if(c.dateValue < s){
37869                 return ;
37870             }
37871             ret.push(c);
37872         });
37873         
37874         return ret;    
37875     },
37876     
37877     findBestRow: function(cells)
37878     {
37879         var ret = 0;
37880         
37881         for (var i =0 ; i < cells.length;i++) {
37882             ret  = Math.max(cells[i].rows || 0,ret);
37883         }
37884         return ret;
37885         
37886     },
37887     
37888     
37889     addItem : function(rec)
37890     {
37891         // look for vertical location slot in
37892         var cells = this.findCells(rec);
37893         
37894         rec.row = this.findBestRow(cells);
37895         
37896         // work out the location.
37897         
37898         var crow = false;
37899         var rows = [];
37900         for(var i =0; i < cells.length; i++) {
37901             if (!crow) {
37902                 crow = {
37903                     start : cells[i],
37904                     end :  cells[i]
37905                 };
37906                 continue;
37907             }
37908             if (crow.start.getY() == cells[i].getY()) {
37909                 // on same row.
37910                 crow.end = cells[i];
37911                 continue;
37912             }
37913             // different row.
37914             rows.push(crow);
37915             crow = {
37916                 start: cells[i],
37917                 end : cells[i]
37918             };
37919             
37920         }
37921         
37922         rows.push(crow);
37923         rec.els = [];
37924         rec.rows = rows;
37925         rec.cells = cells;
37926         for (var i = 0; i < cells.length;i++) {
37927             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37928             
37929         }
37930         
37931         
37932     },
37933     
37934     clearEvents: function() {
37935         
37936         if (!this.eventStore.getCount()) {
37937             return;
37938         }
37939         // reset number of rows in cells.
37940         Roo.each(this.cells.elements, function(c){
37941             c.rows = 0;
37942         });
37943         
37944         this.eventStore.each(function(e) {
37945             this.clearEvent(e);
37946         },this);
37947         
37948     },
37949     
37950     clearEvent : function(ev)
37951     {
37952         if (ev.els) {
37953             Roo.each(ev.els, function(el) {
37954                 el.un('mouseenter' ,this.onEventEnter, this);
37955                 el.un('mouseleave' ,this.onEventLeave, this);
37956                 el.remove();
37957             },this);
37958             ev.els = [];
37959         }
37960     },
37961     
37962     
37963     renderEvent : function(ev,ctr) {
37964         if (!ctr) {
37965              ctr = this.view.el.select('.fc-event-container',true).first();
37966         }
37967         
37968          
37969         this.clearEvent(ev);
37970             //code
37971        
37972         
37973         
37974         ev.els = [];
37975         var cells = ev.cells;
37976         var rows = ev.rows;
37977         this.fireEvent('eventrender', this, ev);
37978         
37979         for(var i =0; i < rows.length; i++) {
37980             
37981             cls = '';
37982             if (i == 0) {
37983                 cls += ' fc-event-start';
37984             }
37985             if ((i+1) == rows.length) {
37986                 cls += ' fc-event-end';
37987             }
37988             
37989             //Roo.log(ev.data);
37990             // how many rows should it span..
37991             var cg = this.eventTmpl.append(ctr,Roo.apply({
37992                 fccls : cls
37993                 
37994             }, ev.data) , true);
37995             
37996             
37997             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37998             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37999             cg.on('click', this.onEventClick, this, ev);
38000             
38001             ev.els.push(cg);
38002             
38003             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38004             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38005             //Roo.log(cg);
38006              
38007             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38008             cg.setWidth(ebox.right - sbox.x -2);
38009         }
38010     },
38011     
38012     renderEvents: function()
38013     {   
38014         // first make sure there is enough space..
38015         
38016         if (!this.eventTmpl) {
38017             this.eventTmpl = new Roo.Template(
38018                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38019                     '<div class="fc-event-inner">' +
38020                         '<span class="fc-event-time">{time}</span>' +
38021                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38022                     '</div>' +
38023                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38024                 '</div>'
38025             );
38026                 
38027         }
38028                
38029         
38030         
38031         this.cells.each(function(c) {
38032             //Roo.log(c.select('.fc-day-content div',true).first());
38033             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38034         });
38035         
38036         var ctr = this.view.el.select('.fc-event-container',true).first();
38037         
38038         var cls;
38039         this.eventStore.each(function(ev){
38040             
38041             this.renderEvent(ev);
38042              
38043              
38044         }, this);
38045         this.view.layout();
38046         
38047     },
38048     
38049     onEventEnter: function (e, el,event,d) {
38050         this.fireEvent('evententer', this, el, event);
38051     },
38052     
38053     onEventLeave: function (e, el,event,d) {
38054         this.fireEvent('eventleave', this, el, event);
38055     },
38056     
38057     onEventClick: function (e, el,event,d) {
38058         this.fireEvent('eventclick', this, el, event);
38059     },
38060     
38061     onMonthChange: function () {
38062         this.store.load();
38063     },
38064     
38065     onLoad: function () {
38066         
38067         //Roo.log('calendar onload');
38068 //         
38069         if(this.eventStore.getCount() > 0){
38070             
38071            
38072             
38073             this.eventStore.each(function(d){
38074                 
38075                 
38076                 // FIXME..
38077                 var add =   d.data;
38078                 if (typeof(add.end_dt) == 'undefined')  {
38079                     Roo.log("Missing End time in calendar data: ");
38080                     Roo.log(d);
38081                     return;
38082                 }
38083                 if (typeof(add.start_dt) == 'undefined')  {
38084                     Roo.log("Missing Start time in calendar data: ");
38085                     Roo.log(d);
38086                     return;
38087                 }
38088                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38089                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38090                 add.id = add.id || d.id;
38091                 add.title = add.title || '??';
38092                 
38093                 this.addItem(d);
38094                 
38095              
38096             },this);
38097         }
38098         
38099         this.renderEvents();
38100     }
38101     
38102
38103 });
38104 /*
38105  grid : {
38106                 xtype: 'Grid',
38107                 xns: Roo.grid,
38108                 listeners : {
38109                     render : function ()
38110                     {
38111                         _this.grid = this;
38112                         
38113                         if (!this.view.el.hasClass('course-timesheet')) {
38114                             this.view.el.addClass('course-timesheet');
38115                         }
38116                         if (this.tsStyle) {
38117                             this.ds.load({});
38118                             return; 
38119                         }
38120                         Roo.log('width');
38121                         Roo.log(_this.grid.view.el.getWidth());
38122                         
38123                         
38124                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38125                             '.course-timesheet .x-grid-row' : {
38126                                 height: '80px'
38127                             },
38128                             '.x-grid-row td' : {
38129                                 'vertical-align' : 0
38130                             },
38131                             '.course-edit-link' : {
38132                                 'color' : 'blue',
38133                                 'text-overflow' : 'ellipsis',
38134                                 'overflow' : 'hidden',
38135                                 'white-space' : 'nowrap',
38136                                 'cursor' : 'pointer'
38137                             },
38138                             '.sub-link' : {
38139                                 'color' : 'green'
38140                             },
38141                             '.de-act-sup-link' : {
38142                                 'color' : 'purple',
38143                                 'text-decoration' : 'line-through'
38144                             },
38145                             '.de-act-link' : {
38146                                 'color' : 'red',
38147                                 'text-decoration' : 'line-through'
38148                             },
38149                             '.course-timesheet .course-highlight' : {
38150                                 'border-top-style': 'dashed !important',
38151                                 'border-bottom-bottom': 'dashed !important'
38152                             },
38153                             '.course-timesheet .course-item' : {
38154                                 'font-family'   : 'tahoma, arial, helvetica',
38155                                 'font-size'     : '11px',
38156                                 'overflow'      : 'hidden',
38157                                 'padding-left'  : '10px',
38158                                 'padding-right' : '10px',
38159                                 'padding-top' : '10px' 
38160                             }
38161                             
38162                         }, Roo.id());
38163                                 this.ds.load({});
38164                     }
38165                 },
38166                 autoWidth : true,
38167                 monitorWindowResize : false,
38168                 cellrenderer : function(v,x,r)
38169                 {
38170                     return v;
38171                 },
38172                 sm : {
38173                     xtype: 'CellSelectionModel',
38174                     xns: Roo.grid
38175                 },
38176                 dataSource : {
38177                     xtype: 'Store',
38178                     xns: Roo.data,
38179                     listeners : {
38180                         beforeload : function (_self, options)
38181                         {
38182                             options.params = options.params || {};
38183                             options.params._month = _this.monthField.getValue();
38184                             options.params.limit = 9999;
38185                             options.params['sort'] = 'when_dt';    
38186                             options.params['dir'] = 'ASC';    
38187                             this.proxy.loadResponse = this.loadResponse;
38188                             Roo.log("load?");
38189                             //this.addColumns();
38190                         },
38191                         load : function (_self, records, options)
38192                         {
38193                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38194                                 // if you click on the translation.. you can edit it...
38195                                 var el = Roo.get(this);
38196                                 var id = el.dom.getAttribute('data-id');
38197                                 var d = el.dom.getAttribute('data-date');
38198                                 var t = el.dom.getAttribute('data-time');
38199                                 //var id = this.child('span').dom.textContent;
38200                                 
38201                                 //Roo.log(this);
38202                                 Pman.Dialog.CourseCalendar.show({
38203                                     id : id,
38204                                     when_d : d,
38205                                     when_t : t,
38206                                     productitem_active : id ? 1 : 0
38207                                 }, function() {
38208                                     _this.grid.ds.load({});
38209                                 });
38210                            
38211                            });
38212                            
38213                            _this.panel.fireEvent('resize', [ '', '' ]);
38214                         }
38215                     },
38216                     loadResponse : function(o, success, response){
38217                             // this is overridden on before load..
38218                             
38219                             Roo.log("our code?");       
38220                             //Roo.log(success);
38221                             //Roo.log(response)
38222                             delete this.activeRequest;
38223                             if(!success){
38224                                 this.fireEvent("loadexception", this, o, response);
38225                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38226                                 return;
38227                             }
38228                             var result;
38229                             try {
38230                                 result = o.reader.read(response);
38231                             }catch(e){
38232                                 Roo.log("load exception?");
38233                                 this.fireEvent("loadexception", this, o, response, e);
38234                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38235                                 return;
38236                             }
38237                             Roo.log("ready...");        
38238                             // loop through result.records;
38239                             // and set this.tdate[date] = [] << array of records..
38240                             _this.tdata  = {};
38241                             Roo.each(result.records, function(r){
38242                                 //Roo.log(r.data);
38243                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38244                                     _this.tdata[r.data.when_dt.format('j')] = [];
38245                                 }
38246                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38247                             });
38248                             
38249                             //Roo.log(_this.tdata);
38250                             
38251                             result.records = [];
38252                             result.totalRecords = 6;
38253                     
38254                             // let's generate some duumy records for the rows.
38255                             //var st = _this.dateField.getValue();
38256                             
38257                             // work out monday..
38258                             //st = st.add(Date.DAY, -1 * st.format('w'));
38259                             
38260                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38261                             
38262                             var firstOfMonth = date.getFirstDayOfMonth();
38263                             var days = date.getDaysInMonth();
38264                             var d = 1;
38265                             var firstAdded = false;
38266                             for (var i = 0; i < result.totalRecords ; i++) {
38267                                 //var d= st.add(Date.DAY, i);
38268                                 var row = {};
38269                                 var added = 0;
38270                                 for(var w = 0 ; w < 7 ; w++){
38271                                     if(!firstAdded && firstOfMonth != w){
38272                                         continue;
38273                                     }
38274                                     if(d > days){
38275                                         continue;
38276                                     }
38277                                     firstAdded = true;
38278                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38279                                     row['weekday'+w] = String.format(
38280                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38281                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38282                                                     d,
38283                                                     date.format('Y-m-')+dd
38284                                                 );
38285                                     added++;
38286                                     if(typeof(_this.tdata[d]) != 'undefined'){
38287                                         Roo.each(_this.tdata[d], function(r){
38288                                             var is_sub = '';
38289                                             var deactive = '';
38290                                             var id = r.id;
38291                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38292                                             if(r.parent_id*1>0){
38293                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38294                                                 id = r.parent_id;
38295                                             }
38296                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38297                                                 deactive = 'de-act-link';
38298                                             }
38299                                             
38300                                             row['weekday'+w] += String.format(
38301                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38302                                                     id, //0
38303                                                     r.product_id_name, //1
38304                                                     r.when_dt.format('h:ia'), //2
38305                                                     is_sub, //3
38306                                                     deactive, //4
38307                                                     desc // 5
38308                                             );
38309                                         });
38310                                     }
38311                                     d++;
38312                                 }
38313                                 
38314                                 // only do this if something added..
38315                                 if(added > 0){ 
38316                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38317                                 }
38318                                 
38319                                 
38320                                 // push it twice. (second one with an hour..
38321                                 
38322                             }
38323                             //Roo.log(result);
38324                             this.fireEvent("load", this, o, o.request.arg);
38325                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38326                         },
38327                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38328                     proxy : {
38329                         xtype: 'HttpProxy',
38330                         xns: Roo.data,
38331                         method : 'GET',
38332                         url : baseURL + '/Roo/Shop_course.php'
38333                     },
38334                     reader : {
38335                         xtype: 'JsonReader',
38336                         xns: Roo.data,
38337                         id : 'id',
38338                         fields : [
38339                             {
38340                                 'name': 'id',
38341                                 'type': 'int'
38342                             },
38343                             {
38344                                 'name': 'when_dt',
38345                                 'type': 'string'
38346                             },
38347                             {
38348                                 'name': 'end_dt',
38349                                 'type': 'string'
38350                             },
38351                             {
38352                                 'name': 'parent_id',
38353                                 'type': 'int'
38354                             },
38355                             {
38356                                 'name': 'product_id',
38357                                 'type': 'int'
38358                             },
38359                             {
38360                                 'name': 'productitem_id',
38361                                 'type': 'int'
38362                             },
38363                             {
38364                                 'name': 'guid',
38365                                 'type': 'int'
38366                             }
38367                         ]
38368                     }
38369                 },
38370                 toolbar : {
38371                     xtype: 'Toolbar',
38372                     xns: Roo,
38373                     items : [
38374                         {
38375                             xtype: 'Button',
38376                             xns: Roo.Toolbar,
38377                             listeners : {
38378                                 click : function (_self, e)
38379                                 {
38380                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38381                                     sd.setMonth(sd.getMonth()-1);
38382                                     _this.monthField.setValue(sd.format('Y-m-d'));
38383                                     _this.grid.ds.load({});
38384                                 }
38385                             },
38386                             text : "Back"
38387                         },
38388                         {
38389                             xtype: 'Separator',
38390                             xns: Roo.Toolbar
38391                         },
38392                         {
38393                             xtype: 'MonthField',
38394                             xns: Roo.form,
38395                             listeners : {
38396                                 render : function (_self)
38397                                 {
38398                                     _this.monthField = _self;
38399                                    // _this.monthField.set  today
38400                                 },
38401                                 select : function (combo, date)
38402                                 {
38403                                     _this.grid.ds.load({});
38404                                 }
38405                             },
38406                             value : (function() { return new Date(); })()
38407                         },
38408                         {
38409                             xtype: 'Separator',
38410                             xns: Roo.Toolbar
38411                         },
38412                         {
38413                             xtype: 'TextItem',
38414                             xns: Roo.Toolbar,
38415                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38416                         },
38417                         {
38418                             xtype: 'Fill',
38419                             xns: Roo.Toolbar
38420                         },
38421                         {
38422                             xtype: 'Button',
38423                             xns: Roo.Toolbar,
38424                             listeners : {
38425                                 click : function (_self, e)
38426                                 {
38427                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38428                                     sd.setMonth(sd.getMonth()+1);
38429                                     _this.monthField.setValue(sd.format('Y-m-d'));
38430                                     _this.grid.ds.load({});
38431                                 }
38432                             },
38433                             text : "Next"
38434                         }
38435                     ]
38436                 },
38437                  
38438             }
38439         };
38440         
38441         *//*
38442  * Based on:
38443  * Ext JS Library 1.1.1
38444  * Copyright(c) 2006-2007, Ext JS, LLC.
38445  *
38446  * Originally Released Under LGPL - original licence link has changed is not relivant.
38447  *
38448  * Fork - LGPL
38449  * <script type="text/javascript">
38450  */
38451  
38452 /**
38453  * @class Roo.LoadMask
38454  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38455  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38456  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38457  * element's UpdateManager load indicator and will be destroyed after the initial load.
38458  * @constructor
38459  * Create a new LoadMask
38460  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38461  * @param {Object} config The config object
38462  */
38463 Roo.LoadMask = function(el, config){
38464     this.el = Roo.get(el);
38465     Roo.apply(this, config);
38466     if(this.store){
38467         this.store.on('beforeload', this.onBeforeLoad, this);
38468         this.store.on('load', this.onLoad, this);
38469         this.store.on('loadexception', this.onLoadException, this);
38470         this.removeMask = false;
38471     }else{
38472         var um = this.el.getUpdateManager();
38473         um.showLoadIndicator = false; // disable the default indicator
38474         um.on('beforeupdate', this.onBeforeLoad, this);
38475         um.on('update', this.onLoad, this);
38476         um.on('failure', this.onLoad, this);
38477         this.removeMask = true;
38478     }
38479 };
38480
38481 Roo.LoadMask.prototype = {
38482     /**
38483      * @cfg {Boolean} removeMask
38484      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38485      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38486      */
38487     removeMask : false,
38488     /**
38489      * @cfg {String} msg
38490      * The text to display in a centered loading message box (defaults to 'Loading...')
38491      */
38492     msg : 'Loading...',
38493     /**
38494      * @cfg {String} msgCls
38495      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38496      */
38497     msgCls : 'x-mask-loading',
38498
38499     /**
38500      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38501      * @type Boolean
38502      */
38503     disabled: false,
38504
38505     /**
38506      * Disables the mask to prevent it from being displayed
38507      */
38508     disable : function(){
38509        this.disabled = true;
38510     },
38511
38512     /**
38513      * Enables the mask so that it can be displayed
38514      */
38515     enable : function(){
38516         this.disabled = false;
38517     },
38518     
38519     onLoadException : function()
38520     {
38521         Roo.log(arguments);
38522         
38523         if (typeof(arguments[3]) != 'undefined') {
38524             Roo.MessageBox.alert("Error loading",arguments[3]);
38525         } 
38526         /*
38527         try {
38528             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38529                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38530             }   
38531         } catch(e) {
38532             
38533         }
38534         */
38535     
38536         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38537     },
38538     // private
38539     onLoad : function()
38540     {
38541         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38542     },
38543
38544     // private
38545     onBeforeLoad : function(){
38546         if(!this.disabled){
38547             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38548         }
38549     },
38550
38551     // private
38552     destroy : function(){
38553         if(this.store){
38554             this.store.un('beforeload', this.onBeforeLoad, this);
38555             this.store.un('load', this.onLoad, this);
38556             this.store.un('loadexception', this.onLoadException, this);
38557         }else{
38558             var um = this.el.getUpdateManager();
38559             um.un('beforeupdate', this.onBeforeLoad, this);
38560             um.un('update', this.onLoad, this);
38561             um.un('failure', this.onLoad, this);
38562         }
38563     }
38564 };/*
38565  * Based on:
38566  * Ext JS Library 1.1.1
38567  * Copyright(c) 2006-2007, Ext JS, LLC.
38568  *
38569  * Originally Released Under LGPL - original licence link has changed is not relivant.
38570  *
38571  * Fork - LGPL
38572  * <script type="text/javascript">
38573  */
38574
38575
38576 /**
38577  * @class Roo.XTemplate
38578  * @extends Roo.Template
38579  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38580 <pre><code>
38581 var t = new Roo.XTemplate(
38582         '&lt;select name="{name}"&gt;',
38583                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38584         '&lt;/select&gt;'
38585 );
38586  
38587 // then append, applying the master template values
38588  </code></pre>
38589  *
38590  * Supported features:
38591  *
38592  *  Tags:
38593
38594 <pre><code>
38595       {a_variable} - output encoded.
38596       {a_variable.format:("Y-m-d")} - call a method on the variable
38597       {a_variable:raw} - unencoded output
38598       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38599       {a_variable:this.method_on_template(...)} - call a method on the template object.
38600  
38601 </code></pre>
38602  *  The tpl tag:
38603 <pre><code>
38604         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38605         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38606         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38607         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38608   
38609         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38610         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38611 </code></pre>
38612  *      
38613  */
38614 Roo.XTemplate = function()
38615 {
38616     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38617     if (this.html) {
38618         this.compile();
38619     }
38620 };
38621
38622
38623 Roo.extend(Roo.XTemplate, Roo.Template, {
38624
38625     /**
38626      * The various sub templates
38627      */
38628     tpls : false,
38629     /**
38630      *
38631      * basic tag replacing syntax
38632      * WORD:WORD()
38633      *
38634      * // you can fake an object call by doing this
38635      *  x.t:(test,tesT) 
38636      * 
38637      */
38638     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38639
38640     /**
38641      * compile the template
38642      *
38643      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38644      *
38645      */
38646     compile: function()
38647     {
38648         var s = this.html;
38649      
38650         s = ['<tpl>', s, '</tpl>'].join('');
38651     
38652         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38653             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38654             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38655             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38656             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38657             m,
38658             id     = 0,
38659             tpls   = [];
38660     
38661         while(true == !!(m = s.match(re))){
38662             var forMatch   = m[0].match(nameRe),
38663                 ifMatch   = m[0].match(ifRe),
38664                 execMatch   = m[0].match(execRe),
38665                 namedMatch   = m[0].match(namedRe),
38666                 
38667                 exp  = null, 
38668                 fn   = null,
38669                 exec = null,
38670                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38671                 
38672             if (ifMatch) {
38673                 // if - puts fn into test..
38674                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38675                 if(exp){
38676                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38677                 }
38678             }
38679             
38680             if (execMatch) {
38681                 // exec - calls a function... returns empty if true is  returned.
38682                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38683                 if(exp){
38684                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38685                 }
38686             }
38687             
38688             
38689             if (name) {
38690                 // for = 
38691                 switch(name){
38692                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38693                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38694                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38695                 }
38696             }
38697             var uid = namedMatch ? namedMatch[1] : id;
38698             
38699             
38700             tpls.push({
38701                 id:     namedMatch ? namedMatch[1] : id,
38702                 target: name,
38703                 exec:   exec,
38704                 test:   fn,
38705                 body:   m[1] || ''
38706             });
38707             if (namedMatch) {
38708                 s = s.replace(m[0], '');
38709             } else { 
38710                 s = s.replace(m[0], '{xtpl'+ id + '}');
38711             }
38712             ++id;
38713         }
38714         this.tpls = [];
38715         for(var i = tpls.length-1; i >= 0; --i){
38716             this.compileTpl(tpls[i]);
38717             this.tpls[tpls[i].id] = tpls[i];
38718         }
38719         this.master = tpls[tpls.length-1];
38720         return this;
38721     },
38722     /**
38723      * same as applyTemplate, except it's done to one of the subTemplates
38724      * when using named templates, you can do:
38725      *
38726      * var str = pl.applySubTemplate('your-name', values);
38727      *
38728      * 
38729      * @param {Number} id of the template
38730      * @param {Object} values to apply to template
38731      * @param {Object} parent (normaly the instance of this object)
38732      */
38733     applySubTemplate : function(id, values, parent)
38734     {
38735         
38736         
38737         var t = this.tpls[id];
38738         
38739         
38740         try { 
38741             if(t.test && !t.test.call(this, values, parent)){
38742                 return '';
38743             }
38744         } catch(e) {
38745             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38746             Roo.log(e.toString());
38747             Roo.log(t.test);
38748             return ''
38749         }
38750         try { 
38751             
38752             if(t.exec && t.exec.call(this, values, parent)){
38753                 return '';
38754             }
38755         } catch(e) {
38756             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38757             Roo.log(e.toString());
38758             Roo.log(t.exec);
38759             return ''
38760         }
38761         try {
38762             var vs = t.target ? t.target.call(this, values, parent) : values;
38763             parent = t.target ? values : parent;
38764             if(t.target && vs instanceof Array){
38765                 var buf = [];
38766                 for(var i = 0, len = vs.length; i < len; i++){
38767                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38768                 }
38769                 return buf.join('');
38770             }
38771             return t.compiled.call(this, vs, parent);
38772         } catch (e) {
38773             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38774             Roo.log(e.toString());
38775             Roo.log(t.compiled);
38776             return '';
38777         }
38778     },
38779
38780     compileTpl : function(tpl)
38781     {
38782         var fm = Roo.util.Format;
38783         var useF = this.disableFormats !== true;
38784         var sep = Roo.isGecko ? "+" : ",";
38785         var undef = function(str) {
38786             Roo.log("Property not found :"  + str);
38787             return '';
38788         };
38789         
38790         var fn = function(m, name, format, args)
38791         {
38792             //Roo.log(arguments);
38793             args = args ? args.replace(/\\'/g,"'") : args;
38794             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38795             if (typeof(format) == 'undefined') {
38796                 format= 'htmlEncode';
38797             }
38798             if (format == 'raw' ) {
38799                 format = false;
38800             }
38801             
38802             if(name.substr(0, 4) == 'xtpl'){
38803                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38804             }
38805             
38806             // build an array of options to determine if value is undefined..
38807             
38808             // basically get 'xxxx.yyyy' then do
38809             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38810             //    (function () { Roo.log("Property not found"); return ''; })() :
38811             //    ......
38812             
38813             var udef_ar = [];
38814             var lookfor = '';
38815             Roo.each(name.split('.'), function(st) {
38816                 lookfor += (lookfor.length ? '.': '') + st;
38817                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38818             });
38819             
38820             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38821             
38822             
38823             if(format && useF){
38824                 
38825                 args = args ? ',' + args : "";
38826                  
38827                 if(format.substr(0, 5) != "this."){
38828                     format = "fm." + format + '(';
38829                 }else{
38830                     format = 'this.call("'+ format.substr(5) + '", ';
38831                     args = ", values";
38832                 }
38833                 
38834                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38835             }
38836              
38837             if (args.length) {
38838                 // called with xxyx.yuu:(test,test)
38839                 // change to ()
38840                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38841             }
38842             // raw.. - :raw modifier..
38843             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38844             
38845         };
38846         var body;
38847         // branched to use + in gecko and [].join() in others
38848         if(Roo.isGecko){
38849             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38850                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38851                     "';};};";
38852         }else{
38853             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38854             body.push(tpl.body.replace(/(\r\n|\n)/g,
38855                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38856             body.push("'].join('');};};");
38857             body = body.join('');
38858         }
38859         
38860         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38861        
38862         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38863         eval(body);
38864         
38865         return this;
38866     },
38867
38868     applyTemplate : function(values){
38869         return this.master.compiled.call(this, values, {});
38870         //var s = this.subs;
38871     },
38872
38873     apply : function(){
38874         return this.applyTemplate.apply(this, arguments);
38875     }
38876
38877  });
38878
38879 Roo.XTemplate.from = function(el){
38880     el = Roo.getDom(el);
38881     return new Roo.XTemplate(el.value || el.innerHTML);
38882 };