better support for mailchimp emails
[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 {Mixed} 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  * Basic Toolbar class.
6995  * @constructor
6996  * Creates a new Toolbar
6997  * @param {Object} container The config object
6998  */ 
6999 Roo.Toolbar = function(container, buttons, config)
7000 {
7001     /// old consturctor format still supported..
7002     if(container instanceof Array){ // omit the container for later rendering
7003         buttons = container;
7004         config = buttons;
7005         container = null;
7006     }
7007     if (typeof(container) == 'object' && container.xtype) {
7008         config = container;
7009         container = config.container;
7010         buttons = config.buttons || []; // not really - use items!!
7011     }
7012     var xitems = [];
7013     if (config && config.items) {
7014         xitems = config.items;
7015         delete config.items;
7016     }
7017     Roo.apply(this, config);
7018     this.buttons = buttons;
7019     
7020     if(container){
7021         this.render(container);
7022     }
7023     this.xitems = xitems;
7024     Roo.each(xitems, function(b) {
7025         this.add(b);
7026     }, this);
7027     
7028 };
7029
7030 Roo.Toolbar.prototype = {
7031     /**
7032      * @cfg {Array} items
7033      * array of button configs or elements to add (will be converted to a MixedCollection)
7034      */
7035     items: false,
7036     /**
7037      * @cfg {String/HTMLElement/Element} container
7038      * The id or element that will contain the toolbar
7039      */
7040     // private
7041     render : function(ct){
7042         this.el = Roo.get(ct);
7043         if(this.cls){
7044             this.el.addClass(this.cls);
7045         }
7046         // using a table allows for vertical alignment
7047         // 100% width is needed by Safari...
7048         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7049         this.tr = this.el.child("tr", true);
7050         var autoId = 0;
7051         this.items = new Roo.util.MixedCollection(false, function(o){
7052             return o.id || ("item" + (++autoId));
7053         });
7054         if(this.buttons){
7055             this.add.apply(this, this.buttons);
7056             delete this.buttons;
7057         }
7058     },
7059
7060     /**
7061      * Adds element(s) to the toolbar -- this function takes a variable number of 
7062      * arguments of mixed type and adds them to the toolbar.
7063      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7064      * <ul>
7065      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7066      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7067      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7068      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7069      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7070      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7071      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7072      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7073      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7074      * </ul>
7075      * @param {Mixed} arg2
7076      * @param {Mixed} etc.
7077      */
7078     add : function(){
7079         var a = arguments, l = a.length;
7080         for(var i = 0; i < l; i++){
7081             this._add(a[i]);
7082         }
7083     },
7084     // private..
7085     _add : function(el) {
7086         
7087         if (el.xtype) {
7088             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7089         }
7090         
7091         if (el.applyTo){ // some kind of form field
7092             return this.addField(el);
7093         } 
7094         if (el.render){ // some kind of Toolbar.Item
7095             return this.addItem(el);
7096         }
7097         if (typeof el == "string"){ // string
7098             if(el == "separator" || el == "-"){
7099                 return this.addSeparator();
7100             }
7101             if (el == " "){
7102                 return this.addSpacer();
7103             }
7104             if(el == "->"){
7105                 return this.addFill();
7106             }
7107             return this.addText(el);
7108             
7109         }
7110         if(el.tagName){ // element
7111             return this.addElement(el);
7112         }
7113         if(typeof el == "object"){ // must be button config?
7114             return this.addButton(el);
7115         }
7116         // and now what?!?!
7117         return false;
7118         
7119     },
7120     
7121     /**
7122      * Add an Xtype element
7123      * @param {Object} xtype Xtype Object
7124      * @return {Object} created Object
7125      */
7126     addxtype : function(e){
7127         return this.add(e);  
7128     },
7129     
7130     /**
7131      * Returns the Element for this toolbar.
7132      * @return {Roo.Element}
7133      */
7134     getEl : function(){
7135         return this.el;  
7136     },
7137     
7138     /**
7139      * Adds a separator
7140      * @return {Roo.Toolbar.Item} The separator item
7141      */
7142     addSeparator : function(){
7143         return this.addItem(new Roo.Toolbar.Separator());
7144     },
7145
7146     /**
7147      * Adds a spacer element
7148      * @return {Roo.Toolbar.Spacer} The spacer item
7149      */
7150     addSpacer : function(){
7151         return this.addItem(new Roo.Toolbar.Spacer());
7152     },
7153
7154     /**
7155      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7156      * @return {Roo.Toolbar.Fill} The fill item
7157      */
7158     addFill : function(){
7159         return this.addItem(new Roo.Toolbar.Fill());
7160     },
7161
7162     /**
7163      * Adds any standard HTML element to the toolbar
7164      * @param {String/HTMLElement/Element} el The element or id of the element to add
7165      * @return {Roo.Toolbar.Item} The element's item
7166      */
7167     addElement : function(el){
7168         return this.addItem(new Roo.Toolbar.Item(el));
7169     },
7170     /**
7171      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7172      * @type Roo.util.MixedCollection  
7173      */
7174     items : false,
7175      
7176     /**
7177      * Adds any Toolbar.Item or subclass
7178      * @param {Roo.Toolbar.Item} item
7179      * @return {Roo.Toolbar.Item} The item
7180      */
7181     addItem : function(item){
7182         var td = this.nextBlock();
7183         item.render(td);
7184         this.items.add(item);
7185         return item;
7186     },
7187     
7188     /**
7189      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7190      * @param {Object/Array} config A button config or array of configs
7191      * @return {Roo.Toolbar.Button/Array}
7192      */
7193     addButton : function(config){
7194         if(config instanceof Array){
7195             var buttons = [];
7196             for(var i = 0, len = config.length; i < len; i++) {
7197                 buttons.push(this.addButton(config[i]));
7198             }
7199             return buttons;
7200         }
7201         var b = config;
7202         if(!(config instanceof Roo.Toolbar.Button)){
7203             b = config.split ?
7204                 new Roo.Toolbar.SplitButton(config) :
7205                 new Roo.Toolbar.Button(config);
7206         }
7207         var td = this.nextBlock();
7208         b.render(td);
7209         this.items.add(b);
7210         return b;
7211     },
7212     
7213     /**
7214      * Adds text to the toolbar
7215      * @param {String} text The text to add
7216      * @return {Roo.Toolbar.Item} The element's item
7217      */
7218     addText : function(text){
7219         return this.addItem(new Roo.Toolbar.TextItem(text));
7220     },
7221     
7222     /**
7223      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7224      * @param {Number} index The index where the item is to be inserted
7225      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7226      * @return {Roo.Toolbar.Button/Item}
7227      */
7228     insertButton : function(index, item){
7229         if(item instanceof Array){
7230             var buttons = [];
7231             for(var i = 0, len = item.length; i < len; i++) {
7232                buttons.push(this.insertButton(index + i, item[i]));
7233             }
7234             return buttons;
7235         }
7236         if (!(item instanceof Roo.Toolbar.Button)){
7237            item = new Roo.Toolbar.Button(item);
7238         }
7239         var td = document.createElement("td");
7240         this.tr.insertBefore(td, this.tr.childNodes[index]);
7241         item.render(td);
7242         this.items.insert(index, item);
7243         return item;
7244     },
7245     
7246     /**
7247      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7248      * @param {Object} config
7249      * @return {Roo.Toolbar.Item} The element's item
7250      */
7251     addDom : function(config, returnEl){
7252         var td = this.nextBlock();
7253         Roo.DomHelper.overwrite(td, config);
7254         var ti = new Roo.Toolbar.Item(td.firstChild);
7255         ti.render(td);
7256         this.items.add(ti);
7257         return ti;
7258     },
7259
7260     /**
7261      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7262      * @type Roo.util.MixedCollection  
7263      */
7264     fields : false,
7265     
7266     /**
7267      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7268      * Note: the field should not have been rendered yet. For a field that has already been
7269      * rendered, use {@link #addElement}.
7270      * @param {Roo.form.Field} field
7271      * @return {Roo.ToolbarItem}
7272      */
7273      
7274       
7275     addField : function(field) {
7276         if (!this.fields) {
7277             var autoId = 0;
7278             this.fields = new Roo.util.MixedCollection(false, function(o){
7279                 return o.id || ("item" + (++autoId));
7280             });
7281
7282         }
7283         
7284         var td = this.nextBlock();
7285         field.render(td);
7286         var ti = new Roo.Toolbar.Item(td.firstChild);
7287         ti.render(td);
7288         this.items.add(ti);
7289         this.fields.add(field);
7290         return ti;
7291     },
7292     /**
7293      * Hide the toolbar
7294      * @method hide
7295      */
7296      
7297       
7298     hide : function()
7299     {
7300         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7301         this.el.child('div').hide();
7302     },
7303     /**
7304      * Show the toolbar
7305      * @method show
7306      */
7307     show : function()
7308     {
7309         this.el.child('div').show();
7310     },
7311       
7312     // private
7313     nextBlock : function(){
7314         var td = document.createElement("td");
7315         this.tr.appendChild(td);
7316         return td;
7317     },
7318
7319     // private
7320     destroy : function(){
7321         if(this.items){ // rendered?
7322             Roo.destroy.apply(Roo, this.items.items);
7323         }
7324         if(this.fields){ // rendered?
7325             Roo.destroy.apply(Roo, this.fields.items);
7326         }
7327         Roo.Element.uncache(this.el, this.tr);
7328     }
7329 };
7330
7331 /**
7332  * @class Roo.Toolbar.Item
7333  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7334  * @constructor
7335  * Creates a new Item
7336  * @param {HTMLElement} el 
7337  */
7338 Roo.Toolbar.Item = function(el){
7339     var cfg = {};
7340     if (typeof (el.xtype) != 'undefined') {
7341         cfg = el;
7342         el = cfg.el;
7343     }
7344     
7345     this.el = Roo.getDom(el);
7346     this.id = Roo.id(this.el);
7347     this.hidden = false;
7348     
7349     this.addEvents({
7350          /**
7351              * @event render
7352              * Fires when the button is rendered
7353              * @param {Button} this
7354              */
7355         'render': true
7356     });
7357     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7358 };
7359 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7360 //Roo.Toolbar.Item.prototype = {
7361     
7362     /**
7363      * Get this item's HTML Element
7364      * @return {HTMLElement}
7365      */
7366     getEl : function(){
7367        return this.el;  
7368     },
7369
7370     // private
7371     render : function(td){
7372         
7373          this.td = td;
7374         td.appendChild(this.el);
7375         
7376         this.fireEvent('render', this);
7377     },
7378     
7379     /**
7380      * Removes and destroys this item.
7381      */
7382     destroy : function(){
7383         this.td.parentNode.removeChild(this.td);
7384     },
7385     
7386     /**
7387      * Shows this item.
7388      */
7389     show: function(){
7390         this.hidden = false;
7391         this.td.style.display = "";
7392     },
7393     
7394     /**
7395      * Hides this item.
7396      */
7397     hide: function(){
7398         this.hidden = true;
7399         this.td.style.display = "none";
7400     },
7401     
7402     /**
7403      * Convenience function for boolean show/hide.
7404      * @param {Boolean} visible true to show/false to hide
7405      */
7406     setVisible: function(visible){
7407         if(visible) {
7408             this.show();
7409         }else{
7410             this.hide();
7411         }
7412     },
7413     
7414     /**
7415      * Try to focus this item.
7416      */
7417     focus : function(){
7418         Roo.fly(this.el).focus();
7419     },
7420     
7421     /**
7422      * Disables this item.
7423      */
7424     disable : function(){
7425         Roo.fly(this.td).addClass("x-item-disabled");
7426         this.disabled = true;
7427         this.el.disabled = true;
7428     },
7429     
7430     /**
7431      * Enables this item.
7432      */
7433     enable : function(){
7434         Roo.fly(this.td).removeClass("x-item-disabled");
7435         this.disabled = false;
7436         this.el.disabled = false;
7437     }
7438 });
7439
7440
7441 /**
7442  * @class Roo.Toolbar.Separator
7443  * @extends Roo.Toolbar.Item
7444  * A simple toolbar separator class
7445  * @constructor
7446  * Creates a new Separator
7447  */
7448 Roo.Toolbar.Separator = function(cfg){
7449     
7450     var s = document.createElement("span");
7451     s.className = "ytb-sep";
7452     if (cfg) {
7453         cfg.el = s;
7454     }
7455     
7456     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7457 };
7458 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7459     enable:Roo.emptyFn,
7460     disable:Roo.emptyFn,
7461     focus:Roo.emptyFn
7462 });
7463
7464 /**
7465  * @class Roo.Toolbar.Spacer
7466  * @extends Roo.Toolbar.Item
7467  * A simple element that adds extra horizontal space to a toolbar.
7468  * @constructor
7469  * Creates a new Spacer
7470  */
7471 Roo.Toolbar.Spacer = function(cfg){
7472     var s = document.createElement("div");
7473     s.className = "ytb-spacer";
7474     if (cfg) {
7475         cfg.el = s;
7476     }
7477     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7478 };
7479 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7480     enable:Roo.emptyFn,
7481     disable:Roo.emptyFn,
7482     focus:Roo.emptyFn
7483 });
7484
7485 /**
7486  * @class Roo.Toolbar.Fill
7487  * @extends Roo.Toolbar.Spacer
7488  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7489  * @constructor
7490  * Creates a new Spacer
7491  */
7492 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7493     // private
7494     render : function(td){
7495         td.style.width = '100%';
7496         Roo.Toolbar.Fill.superclass.render.call(this, td);
7497     }
7498 });
7499
7500 /**
7501  * @class Roo.Toolbar.TextItem
7502  * @extends Roo.Toolbar.Item
7503  * A simple class that renders text directly into a toolbar.
7504  * @constructor
7505  * Creates a new TextItem
7506  * @cfg {string} text 
7507  */
7508 Roo.Toolbar.TextItem = function(cfg){
7509     var  text = cfg || "";
7510     if (typeof(cfg) == 'object') {
7511         text = cfg.text || "";
7512     }  else {
7513         cfg = null;
7514     }
7515     var s = document.createElement("span");
7516     s.className = "ytb-text";
7517     s.innerHTML = text;
7518     if (cfg) {
7519         cfg.el  = s;
7520     }
7521     
7522     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7523 };
7524 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7525     
7526      
7527     enable:Roo.emptyFn,
7528     disable:Roo.emptyFn,
7529     focus:Roo.emptyFn
7530 });
7531
7532 /**
7533  * @class Roo.Toolbar.Button
7534  * @extends Roo.Button
7535  * A button that renders into a toolbar.
7536  * @constructor
7537  * Creates a new Button
7538  * @param {Object} config A standard {@link Roo.Button} config object
7539  */
7540 Roo.Toolbar.Button = function(config){
7541     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7542 };
7543 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7544     render : function(td){
7545         this.td = td;
7546         Roo.Toolbar.Button.superclass.render.call(this, td);
7547     },
7548     
7549     /**
7550      * Removes and destroys this button
7551      */
7552     destroy : function(){
7553         Roo.Toolbar.Button.superclass.destroy.call(this);
7554         this.td.parentNode.removeChild(this.td);
7555     },
7556     
7557     /**
7558      * Shows this button
7559      */
7560     show: function(){
7561         this.hidden = false;
7562         this.td.style.display = "";
7563     },
7564     
7565     /**
7566      * Hides this button
7567      */
7568     hide: function(){
7569         this.hidden = true;
7570         this.td.style.display = "none";
7571     },
7572
7573     /**
7574      * Disables this item
7575      */
7576     disable : function(){
7577         Roo.fly(this.td).addClass("x-item-disabled");
7578         this.disabled = true;
7579     },
7580
7581     /**
7582      * Enables this item
7583      */
7584     enable : function(){
7585         Roo.fly(this.td).removeClass("x-item-disabled");
7586         this.disabled = false;
7587     }
7588 });
7589 // backwards compat
7590 Roo.ToolbarButton = Roo.Toolbar.Button;
7591
7592 /**
7593  * @class Roo.Toolbar.SplitButton
7594  * @extends Roo.SplitButton
7595  * A menu button that renders into a toolbar.
7596  * @constructor
7597  * Creates a new SplitButton
7598  * @param {Object} config A standard {@link Roo.SplitButton} config object
7599  */
7600 Roo.Toolbar.SplitButton = function(config){
7601     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7602 };
7603 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7604     render : function(td){
7605         this.td = td;
7606         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7607     },
7608     
7609     /**
7610      * Removes and destroys this button
7611      */
7612     destroy : function(){
7613         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7614         this.td.parentNode.removeChild(this.td);
7615     },
7616     
7617     /**
7618      * Shows this button
7619      */
7620     show: function(){
7621         this.hidden = false;
7622         this.td.style.display = "";
7623     },
7624     
7625     /**
7626      * Hides this button
7627      */
7628     hide: function(){
7629         this.hidden = true;
7630         this.td.style.display = "none";
7631     }
7632 });
7633
7634 // backwards compat
7635 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7636  * Based on:
7637  * Ext JS Library 1.1.1
7638  * Copyright(c) 2006-2007, Ext JS, LLC.
7639  *
7640  * Originally Released Under LGPL - original licence link has changed is not relivant.
7641  *
7642  * Fork - LGPL
7643  * <script type="text/javascript">
7644  */
7645  
7646 /**
7647  * @class Roo.PagingToolbar
7648  * @extends Roo.Toolbar
7649  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7650  * @constructor
7651  * Create a new PagingToolbar
7652  * @param {Object} config The config object
7653  */
7654 Roo.PagingToolbar = function(el, ds, config)
7655 {
7656     // old args format still supported... - xtype is prefered..
7657     if (typeof(el) == 'object' && el.xtype) {
7658         // created from xtype...
7659         config = el;
7660         ds = el.dataSource;
7661         el = config.container;
7662     }
7663     var items = [];
7664     if (config.items) {
7665         items = config.items;
7666         config.items = [];
7667     }
7668     
7669     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7670     this.ds = ds;
7671     this.cursor = 0;
7672     this.renderButtons(this.el);
7673     this.bind(ds);
7674     
7675     // supprot items array.
7676    
7677     Roo.each(items, function(e) {
7678         this.add(Roo.factory(e));
7679     },this);
7680     
7681 };
7682
7683 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7684     /**
7685      * @cfg {Roo.data.Store} dataSource
7686      * The underlying data store providing the paged data
7687      */
7688     /**
7689      * @cfg {String/HTMLElement/Element} container
7690      * container The id or element that will contain the toolbar
7691      */
7692     /**
7693      * @cfg {Boolean} displayInfo
7694      * True to display the displayMsg (defaults to false)
7695      */
7696     /**
7697      * @cfg {Number} pageSize
7698      * The number of records to display per page (defaults to 20)
7699      */
7700     pageSize: 20,
7701     /**
7702      * @cfg {String} displayMsg
7703      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7704      */
7705     displayMsg : 'Displaying {0} - {1} of {2}',
7706     /**
7707      * @cfg {String} emptyMsg
7708      * The message to display when no records are found (defaults to "No data to display")
7709      */
7710     emptyMsg : 'No data to display',
7711     /**
7712      * Customizable piece of the default paging text (defaults to "Page")
7713      * @type String
7714      */
7715     beforePageText : "Page",
7716     /**
7717      * Customizable piece of the default paging text (defaults to "of %0")
7718      * @type String
7719      */
7720     afterPageText : "of {0}",
7721     /**
7722      * Customizable piece of the default paging text (defaults to "First Page")
7723      * @type String
7724      */
7725     firstText : "First Page",
7726     /**
7727      * Customizable piece of the default paging text (defaults to "Previous Page")
7728      * @type String
7729      */
7730     prevText : "Previous Page",
7731     /**
7732      * Customizable piece of the default paging text (defaults to "Next Page")
7733      * @type String
7734      */
7735     nextText : "Next Page",
7736     /**
7737      * Customizable piece of the default paging text (defaults to "Last Page")
7738      * @type String
7739      */
7740     lastText : "Last Page",
7741     /**
7742      * Customizable piece of the default paging text (defaults to "Refresh")
7743      * @type String
7744      */
7745     refreshText : "Refresh",
7746
7747     // private
7748     renderButtons : function(el){
7749         Roo.PagingToolbar.superclass.render.call(this, el);
7750         this.first = this.addButton({
7751             tooltip: this.firstText,
7752             cls: "x-btn-icon x-grid-page-first",
7753             disabled: true,
7754             handler: this.onClick.createDelegate(this, ["first"])
7755         });
7756         this.prev = this.addButton({
7757             tooltip: this.prevText,
7758             cls: "x-btn-icon x-grid-page-prev",
7759             disabled: true,
7760             handler: this.onClick.createDelegate(this, ["prev"])
7761         });
7762         //this.addSeparator();
7763         this.add(this.beforePageText);
7764         this.field = Roo.get(this.addDom({
7765            tag: "input",
7766            type: "text",
7767            size: "3",
7768            value: "1",
7769            cls: "x-grid-page-number"
7770         }).el);
7771         this.field.on("keydown", this.onPagingKeydown, this);
7772         this.field.on("focus", function(){this.dom.select();});
7773         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7774         this.field.setHeight(18);
7775         //this.addSeparator();
7776         this.next = this.addButton({
7777             tooltip: this.nextText,
7778             cls: "x-btn-icon x-grid-page-next",
7779             disabled: true,
7780             handler: this.onClick.createDelegate(this, ["next"])
7781         });
7782         this.last = this.addButton({
7783             tooltip: this.lastText,
7784             cls: "x-btn-icon x-grid-page-last",
7785             disabled: true,
7786             handler: this.onClick.createDelegate(this, ["last"])
7787         });
7788         //this.addSeparator();
7789         this.loading = this.addButton({
7790             tooltip: this.refreshText,
7791             cls: "x-btn-icon x-grid-loading",
7792             handler: this.onClick.createDelegate(this, ["refresh"])
7793         });
7794
7795         if(this.displayInfo){
7796             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7797         }
7798     },
7799
7800     // private
7801     updateInfo : function(){
7802         if(this.displayEl){
7803             var count = this.ds.getCount();
7804             var msg = count == 0 ?
7805                 this.emptyMsg :
7806                 String.format(
7807                     this.displayMsg,
7808                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7809                 );
7810             this.displayEl.update(msg);
7811         }
7812     },
7813
7814     // private
7815     onLoad : function(ds, r, o){
7816        this.cursor = o.params ? o.params.start : 0;
7817        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7818
7819        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7820        this.field.dom.value = ap;
7821        this.first.setDisabled(ap == 1);
7822        this.prev.setDisabled(ap == 1);
7823        this.next.setDisabled(ap == ps);
7824        this.last.setDisabled(ap == ps);
7825        this.loading.enable();
7826        this.updateInfo();
7827     },
7828
7829     // private
7830     getPageData : function(){
7831         var total = this.ds.getTotalCount();
7832         return {
7833             total : total,
7834             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7835             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7836         };
7837     },
7838
7839     // private
7840     onLoadError : function(){
7841         this.loading.enable();
7842     },
7843
7844     // private
7845     onPagingKeydown : function(e){
7846         var k = e.getKey();
7847         var d = this.getPageData();
7848         if(k == e.RETURN){
7849             var v = this.field.dom.value, pageNum;
7850             if(!v || isNaN(pageNum = parseInt(v, 10))){
7851                 this.field.dom.value = d.activePage;
7852                 return;
7853             }
7854             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7855             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7856             e.stopEvent();
7857         }
7858         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))
7859         {
7860           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7861           this.field.dom.value = pageNum;
7862           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7863           e.stopEvent();
7864         }
7865         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7866         {
7867           var v = this.field.dom.value, pageNum; 
7868           var increment = (e.shiftKey) ? 10 : 1;
7869           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7870             increment *= -1;
7871           }
7872           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7873             this.field.dom.value = d.activePage;
7874             return;
7875           }
7876           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7877           {
7878             this.field.dom.value = parseInt(v, 10) + increment;
7879             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7880             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7881           }
7882           e.stopEvent();
7883         }
7884     },
7885
7886     // private
7887     beforeLoad : function(){
7888         if(this.loading){
7889             this.loading.disable();
7890         }
7891     },
7892
7893     // private
7894     onClick : function(which){
7895         var ds = this.ds;
7896         switch(which){
7897             case "first":
7898                 ds.load({params:{start: 0, limit: this.pageSize}});
7899             break;
7900             case "prev":
7901                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7902             break;
7903             case "next":
7904                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7905             break;
7906             case "last":
7907                 var total = ds.getTotalCount();
7908                 var extra = total % this.pageSize;
7909                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7910                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7911             break;
7912             case "refresh":
7913                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7914             break;
7915         }
7916     },
7917
7918     /**
7919      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7920      * @param {Roo.data.Store} store The data store to unbind
7921      */
7922     unbind : function(ds){
7923         ds.un("beforeload", this.beforeLoad, this);
7924         ds.un("load", this.onLoad, this);
7925         ds.un("loadexception", this.onLoadError, this);
7926         ds.un("remove", this.updateInfo, this);
7927         ds.un("add", this.updateInfo, this);
7928         this.ds = undefined;
7929     },
7930
7931     /**
7932      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7933      * @param {Roo.data.Store} store The data store to bind
7934      */
7935     bind : function(ds){
7936         ds.on("beforeload", this.beforeLoad, this);
7937         ds.on("load", this.onLoad, this);
7938         ds.on("loadexception", this.onLoadError, this);
7939         ds.on("remove", this.updateInfo, this);
7940         ds.on("add", this.updateInfo, this);
7941         this.ds = ds;
7942     }
7943 });/*
7944  * Based on:
7945  * Ext JS Library 1.1.1
7946  * Copyright(c) 2006-2007, Ext JS, LLC.
7947  *
7948  * Originally Released Under LGPL - original licence link has changed is not relivant.
7949  *
7950  * Fork - LGPL
7951  * <script type="text/javascript">
7952  */
7953
7954 /**
7955  * @class Roo.Resizable
7956  * @extends Roo.util.Observable
7957  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7958  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7959  * 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
7960  * the element will be wrapped for you automatically.</p>
7961  * <p>Here is the list of valid resize handles:</p>
7962  * <pre>
7963 Value   Description
7964 ------  -------------------
7965  'n'     north
7966  's'     south
7967  'e'     east
7968  'w'     west
7969  'nw'    northwest
7970  'sw'    southwest
7971  'se'    southeast
7972  'ne'    northeast
7973  'hd'    horizontal drag
7974  'all'   all
7975 </pre>
7976  * <p>Here's an example showing the creation of a typical Resizable:</p>
7977  * <pre><code>
7978 var resizer = new Roo.Resizable("element-id", {
7979     handles: 'all',
7980     minWidth: 200,
7981     minHeight: 100,
7982     maxWidth: 500,
7983     maxHeight: 400,
7984     pinned: true
7985 });
7986 resizer.on("resize", myHandler);
7987 </code></pre>
7988  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7989  * resizer.east.setDisplayed(false);</p>
7990  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7991  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7992  * resize operation's new size (defaults to [0, 0])
7993  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7994  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
7995  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
7996  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
7997  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
7998  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
7999  * @cfg {Number} width The width of the element in pixels (defaults to null)
8000  * @cfg {Number} height The height of the element in pixels (defaults to null)
8001  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8002  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8003  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8004  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8005  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8006  * in favor of the handles config option (defaults to false)
8007  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8008  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8009  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8010  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8011  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8012  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8013  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8014  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8015  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8016  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8017  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8018  * @constructor
8019  * Create a new resizable component
8020  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8021  * @param {Object} config configuration options
8022   */
8023 Roo.Resizable = function(el, config)
8024 {
8025     this.el = Roo.get(el);
8026
8027     if(config && config.wrap){
8028         config.resizeChild = this.el;
8029         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8030         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8031         this.el.setStyle("overflow", "hidden");
8032         this.el.setPositioning(config.resizeChild.getPositioning());
8033         config.resizeChild.clearPositioning();
8034         if(!config.width || !config.height){
8035             var csize = config.resizeChild.getSize();
8036             this.el.setSize(csize.width, csize.height);
8037         }
8038         if(config.pinned && !config.adjustments){
8039             config.adjustments = "auto";
8040         }
8041     }
8042
8043     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8044     this.proxy.unselectable();
8045     this.proxy.enableDisplayMode('block');
8046
8047     Roo.apply(this, config);
8048
8049     if(this.pinned){
8050         this.disableTrackOver = true;
8051         this.el.addClass("x-resizable-pinned");
8052     }
8053     // if the element isn't positioned, make it relative
8054     var position = this.el.getStyle("position");
8055     if(position != "absolute" && position != "fixed"){
8056         this.el.setStyle("position", "relative");
8057     }
8058     if(!this.handles){ // no handles passed, must be legacy style
8059         this.handles = 's,e,se';
8060         if(this.multiDirectional){
8061             this.handles += ',n,w';
8062         }
8063     }
8064     if(this.handles == "all"){
8065         this.handles = "n s e w ne nw se sw";
8066     }
8067     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8068     var ps = Roo.Resizable.positions;
8069     for(var i = 0, len = hs.length; i < len; i++){
8070         if(hs[i] && ps[hs[i]]){
8071             var pos = ps[hs[i]];
8072             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8073         }
8074     }
8075     // legacy
8076     this.corner = this.southeast;
8077     
8078     // updateBox = the box can move..
8079     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8080         this.updateBox = true;
8081     }
8082
8083     this.activeHandle = null;
8084
8085     if(this.resizeChild){
8086         if(typeof this.resizeChild == "boolean"){
8087             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8088         }else{
8089             this.resizeChild = Roo.get(this.resizeChild, true);
8090         }
8091     }
8092     
8093     if(this.adjustments == "auto"){
8094         var rc = this.resizeChild;
8095         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8096         if(rc && (hw || hn)){
8097             rc.position("relative");
8098             rc.setLeft(hw ? hw.el.getWidth() : 0);
8099             rc.setTop(hn ? hn.el.getHeight() : 0);
8100         }
8101         this.adjustments = [
8102             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8103             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8104         ];
8105     }
8106
8107     if(this.draggable){
8108         this.dd = this.dynamic ?
8109             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8110         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8111     }
8112
8113     // public events
8114     this.addEvents({
8115         /**
8116          * @event beforeresize
8117          * Fired before resize is allowed. Set enabled to false to cancel resize.
8118          * @param {Roo.Resizable} this
8119          * @param {Roo.EventObject} e The mousedown event
8120          */
8121         "beforeresize" : true,
8122         /**
8123          * @event resizing
8124          * Fired a resizing.
8125          * @param {Roo.Resizable} this
8126          * @param {Number} x The new x position
8127          * @param {Number} y The new y position
8128          * @param {Number} w The new w width
8129          * @param {Number} h The new h hight
8130          * @param {Roo.EventObject} e The mouseup event
8131          */
8132         "resizing" : true,
8133         /**
8134          * @event resize
8135          * Fired after a resize.
8136          * @param {Roo.Resizable} this
8137          * @param {Number} width The new width
8138          * @param {Number} height The new height
8139          * @param {Roo.EventObject} e The mouseup event
8140          */
8141         "resize" : true
8142     });
8143
8144     if(this.width !== null && this.height !== null){
8145         this.resizeTo(this.width, this.height);
8146     }else{
8147         this.updateChildSize();
8148     }
8149     if(Roo.isIE){
8150         this.el.dom.style.zoom = 1;
8151     }
8152     Roo.Resizable.superclass.constructor.call(this);
8153 };
8154
8155 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8156         resizeChild : false,
8157         adjustments : [0, 0],
8158         minWidth : 5,
8159         minHeight : 5,
8160         maxWidth : 10000,
8161         maxHeight : 10000,
8162         enabled : true,
8163         animate : false,
8164         duration : .35,
8165         dynamic : false,
8166         handles : false,
8167         multiDirectional : false,
8168         disableTrackOver : false,
8169         easing : 'easeOutStrong',
8170         widthIncrement : 0,
8171         heightIncrement : 0,
8172         pinned : false,
8173         width : null,
8174         height : null,
8175         preserveRatio : false,
8176         transparent: false,
8177         minX: 0,
8178         minY: 0,
8179         draggable: false,
8180
8181         /**
8182          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8183          */
8184         constrainTo: undefined,
8185         /**
8186          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8187          */
8188         resizeRegion: undefined,
8189
8190
8191     /**
8192      * Perform a manual resize
8193      * @param {Number} width
8194      * @param {Number} height
8195      */
8196     resizeTo : function(width, height){
8197         this.el.setSize(width, height);
8198         this.updateChildSize();
8199         this.fireEvent("resize", this, width, height, null);
8200     },
8201
8202     // private
8203     startSizing : function(e, handle){
8204         this.fireEvent("beforeresize", this, e);
8205         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8206
8207             if(!this.overlay){
8208                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8209                 this.overlay.unselectable();
8210                 this.overlay.enableDisplayMode("block");
8211                 this.overlay.on("mousemove", this.onMouseMove, this);
8212                 this.overlay.on("mouseup", this.onMouseUp, this);
8213             }
8214             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8215
8216             this.resizing = true;
8217             this.startBox = this.el.getBox();
8218             this.startPoint = e.getXY();
8219             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8220                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8221
8222             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8223             this.overlay.show();
8224
8225             if(this.constrainTo) {
8226                 var ct = Roo.get(this.constrainTo);
8227                 this.resizeRegion = ct.getRegion().adjust(
8228                     ct.getFrameWidth('t'),
8229                     ct.getFrameWidth('l'),
8230                     -ct.getFrameWidth('b'),
8231                     -ct.getFrameWidth('r')
8232                 );
8233             }
8234
8235             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8236             this.proxy.show();
8237             this.proxy.setBox(this.startBox);
8238             if(!this.dynamic){
8239                 this.proxy.setStyle('visibility', 'visible');
8240             }
8241         }
8242     },
8243
8244     // private
8245     onMouseDown : function(handle, e){
8246         if(this.enabled){
8247             e.stopEvent();
8248             this.activeHandle = handle;
8249             this.startSizing(e, handle);
8250         }
8251     },
8252
8253     // private
8254     onMouseUp : function(e){
8255         var size = this.resizeElement();
8256         this.resizing = false;
8257         this.handleOut();
8258         this.overlay.hide();
8259         this.proxy.hide();
8260         this.fireEvent("resize", this, size.width, size.height, e);
8261     },
8262
8263     // private
8264     updateChildSize : function(){
8265         
8266         if(this.resizeChild){
8267             var el = this.el;
8268             var child = this.resizeChild;
8269             var adj = this.adjustments;
8270             if(el.dom.offsetWidth){
8271                 var b = el.getSize(true);
8272                 child.setSize(b.width+adj[0], b.height+adj[1]);
8273             }
8274             // Second call here for IE
8275             // The first call enables instant resizing and
8276             // the second call corrects scroll bars if they
8277             // exist
8278             if(Roo.isIE){
8279                 setTimeout(function(){
8280                     if(el.dom.offsetWidth){
8281                         var b = el.getSize(true);
8282                         child.setSize(b.width+adj[0], b.height+adj[1]);
8283                     }
8284                 }, 10);
8285             }
8286         }
8287     },
8288
8289     // private
8290     snap : function(value, inc, min){
8291         if(!inc || !value) {
8292             return value;
8293         }
8294         var newValue = value;
8295         var m = value % inc;
8296         if(m > 0){
8297             if(m > (inc/2)){
8298                 newValue = value + (inc-m);
8299             }else{
8300                 newValue = value - m;
8301             }
8302         }
8303         return Math.max(min, newValue);
8304     },
8305
8306     // private
8307     resizeElement : function(){
8308         var box = this.proxy.getBox();
8309         if(this.updateBox){
8310             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8311         }else{
8312             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8313         }
8314         this.updateChildSize();
8315         if(!this.dynamic){
8316             this.proxy.hide();
8317         }
8318         return box;
8319     },
8320
8321     // private
8322     constrain : function(v, diff, m, mx){
8323         if(v - diff < m){
8324             diff = v - m;
8325         }else if(v - diff > mx){
8326             diff = mx - v;
8327         }
8328         return diff;
8329     },
8330
8331     // private
8332     onMouseMove : function(e){
8333         
8334         if(this.enabled){
8335             try{// try catch so if something goes wrong the user doesn't get hung
8336
8337             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8338                 return;
8339             }
8340
8341             //var curXY = this.startPoint;
8342             var curSize = this.curSize || this.startBox;
8343             var x = this.startBox.x, y = this.startBox.y;
8344             var ox = x, oy = y;
8345             var w = curSize.width, h = curSize.height;
8346             var ow = w, oh = h;
8347             var mw = this.minWidth, mh = this.minHeight;
8348             var mxw = this.maxWidth, mxh = this.maxHeight;
8349             var wi = this.widthIncrement;
8350             var hi = this.heightIncrement;
8351
8352             var eventXY = e.getXY();
8353             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8354             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8355
8356             var pos = this.activeHandle.position;
8357
8358             switch(pos){
8359                 case "east":
8360                     w += diffX;
8361                     w = Math.min(Math.max(mw, w), mxw);
8362                     break;
8363              
8364                 case "south":
8365                     h += diffY;
8366                     h = Math.min(Math.max(mh, h), mxh);
8367                     break;
8368                 case "southeast":
8369                     w += diffX;
8370                     h += diffY;
8371                     w = Math.min(Math.max(mw, w), mxw);
8372                     h = Math.min(Math.max(mh, h), mxh);
8373                     break;
8374                 case "north":
8375                     diffY = this.constrain(h, diffY, mh, mxh);
8376                     y += diffY;
8377                     h -= diffY;
8378                     break;
8379                 case "hdrag":
8380                     
8381                     if (wi) {
8382                         var adiffX = Math.abs(diffX);
8383                         var sub = (adiffX % wi); // how much 
8384                         if (sub > (wi/2)) { // far enough to snap
8385                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8386                         } else {
8387                             // remove difference.. 
8388                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8389                         }
8390                     }
8391                     x += diffX;
8392                     x = Math.max(this.minX, x);
8393                     break;
8394                 case "west":
8395                     diffX = this.constrain(w, diffX, mw, mxw);
8396                     x += diffX;
8397                     w -= diffX;
8398                     break;
8399                 case "northeast":
8400                     w += diffX;
8401                     w = Math.min(Math.max(mw, w), mxw);
8402                     diffY = this.constrain(h, diffY, mh, mxh);
8403                     y += diffY;
8404                     h -= diffY;
8405                     break;
8406                 case "northwest":
8407                     diffX = this.constrain(w, diffX, mw, mxw);
8408                     diffY = this.constrain(h, diffY, mh, mxh);
8409                     y += diffY;
8410                     h -= diffY;
8411                     x += diffX;
8412                     w -= diffX;
8413                     break;
8414                case "southwest":
8415                     diffX = this.constrain(w, diffX, mw, mxw);
8416                     h += diffY;
8417                     h = Math.min(Math.max(mh, h), mxh);
8418                     x += diffX;
8419                     w -= diffX;
8420                     break;
8421             }
8422
8423             var sw = this.snap(w, wi, mw);
8424             var sh = this.snap(h, hi, mh);
8425             if(sw != w || sh != h){
8426                 switch(pos){
8427                     case "northeast":
8428                         y -= sh - h;
8429                     break;
8430                     case "north":
8431                         y -= sh - h;
8432                         break;
8433                     case "southwest":
8434                         x -= sw - w;
8435                     break;
8436                     case "west":
8437                         x -= sw - w;
8438                         break;
8439                     case "northwest":
8440                         x -= sw - w;
8441                         y -= sh - h;
8442                     break;
8443                 }
8444                 w = sw;
8445                 h = sh;
8446             }
8447
8448             if(this.preserveRatio){
8449                 switch(pos){
8450                     case "southeast":
8451                     case "east":
8452                         h = oh * (w/ow);
8453                         h = Math.min(Math.max(mh, h), mxh);
8454                         w = ow * (h/oh);
8455                        break;
8456                     case "south":
8457                         w = ow * (h/oh);
8458                         w = Math.min(Math.max(mw, w), mxw);
8459                         h = oh * (w/ow);
8460                         break;
8461                     case "northeast":
8462                         w = ow * (h/oh);
8463                         w = Math.min(Math.max(mw, w), mxw);
8464                         h = oh * (w/ow);
8465                     break;
8466                     case "north":
8467                         var tw = w;
8468                         w = ow * (h/oh);
8469                         w = Math.min(Math.max(mw, w), mxw);
8470                         h = oh * (w/ow);
8471                         x += (tw - w) / 2;
8472                         break;
8473                     case "southwest":
8474                         h = oh * (w/ow);
8475                         h = Math.min(Math.max(mh, h), mxh);
8476                         var tw = w;
8477                         w = ow * (h/oh);
8478                         x += tw - w;
8479                         break;
8480                     case "west":
8481                         var th = h;
8482                         h = oh * (w/ow);
8483                         h = Math.min(Math.max(mh, h), mxh);
8484                         y += (th - h) / 2;
8485                         var tw = w;
8486                         w = ow * (h/oh);
8487                         x += tw - w;
8488                        break;
8489                     case "northwest":
8490                         var tw = w;
8491                         var th = h;
8492                         h = oh * (w/ow);
8493                         h = Math.min(Math.max(mh, h), mxh);
8494                         w = ow * (h/oh);
8495                         y += th - h;
8496                         x += tw - w;
8497                        break;
8498
8499                 }
8500             }
8501             if (pos == 'hdrag') {
8502                 w = ow;
8503             }
8504             this.proxy.setBounds(x, y, w, h);
8505             if(this.dynamic){
8506                 this.resizeElement();
8507             }
8508             }catch(e){}
8509         }
8510         this.fireEvent("resizing", this, x, y, w, h, e);
8511     },
8512
8513     // private
8514     handleOver : function(){
8515         if(this.enabled){
8516             this.el.addClass("x-resizable-over");
8517         }
8518     },
8519
8520     // private
8521     handleOut : function(){
8522         if(!this.resizing){
8523             this.el.removeClass("x-resizable-over");
8524         }
8525     },
8526
8527     /**
8528      * Returns the element this component is bound to.
8529      * @return {Roo.Element}
8530      */
8531     getEl : function(){
8532         return this.el;
8533     },
8534
8535     /**
8536      * Returns the resizeChild element (or null).
8537      * @return {Roo.Element}
8538      */
8539     getResizeChild : function(){
8540         return this.resizeChild;
8541     },
8542     groupHandler : function()
8543     {
8544         
8545     },
8546     /**
8547      * Destroys this resizable. If the element was wrapped and
8548      * removeEl is not true then the element remains.
8549      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8550      */
8551     destroy : function(removeEl){
8552         this.proxy.remove();
8553         if(this.overlay){
8554             this.overlay.removeAllListeners();
8555             this.overlay.remove();
8556         }
8557         var ps = Roo.Resizable.positions;
8558         for(var k in ps){
8559             if(typeof ps[k] != "function" && this[ps[k]]){
8560                 var h = this[ps[k]];
8561                 h.el.removeAllListeners();
8562                 h.el.remove();
8563             }
8564         }
8565         if(removeEl){
8566             this.el.update("");
8567             this.el.remove();
8568         }
8569     }
8570 });
8571
8572 // private
8573 // hash to map config positions to true positions
8574 Roo.Resizable.positions = {
8575     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8576     hd: "hdrag"
8577 };
8578
8579 // private
8580 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8581     if(!this.tpl){
8582         // only initialize the template if resizable is used
8583         var tpl = Roo.DomHelper.createTemplate(
8584             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8585         );
8586         tpl.compile();
8587         Roo.Resizable.Handle.prototype.tpl = tpl;
8588     }
8589     this.position = pos;
8590     this.rz = rz;
8591     // show north drag fro topdra
8592     var handlepos = pos == 'hdrag' ? 'north' : pos;
8593     
8594     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8595     if (pos == 'hdrag') {
8596         this.el.setStyle('cursor', 'pointer');
8597     }
8598     this.el.unselectable();
8599     if(transparent){
8600         this.el.setOpacity(0);
8601     }
8602     this.el.on("mousedown", this.onMouseDown, this);
8603     if(!disableTrackOver){
8604         this.el.on("mouseover", this.onMouseOver, this);
8605         this.el.on("mouseout", this.onMouseOut, this);
8606     }
8607 };
8608
8609 // private
8610 Roo.Resizable.Handle.prototype = {
8611     afterResize : function(rz){
8612         Roo.log('after?');
8613         // do nothing
8614     },
8615     // private
8616     onMouseDown : function(e){
8617         this.rz.onMouseDown(this, e);
8618     },
8619     // private
8620     onMouseOver : function(e){
8621         this.rz.handleOver(this, e);
8622     },
8623     // private
8624     onMouseOut : function(e){
8625         this.rz.handleOut(this, e);
8626     }
8627 };/*
8628  * Based on:
8629  * Ext JS Library 1.1.1
8630  * Copyright(c) 2006-2007, Ext JS, LLC.
8631  *
8632  * Originally Released Under LGPL - original licence link has changed is not relivant.
8633  *
8634  * Fork - LGPL
8635  * <script type="text/javascript">
8636  */
8637
8638 /**
8639  * @class Roo.Editor
8640  * @extends Roo.Component
8641  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8642  * @constructor
8643  * Create a new Editor
8644  * @param {Roo.form.Field} field The Field object (or descendant)
8645  * @param {Object} config The config object
8646  */
8647 Roo.Editor = function(field, config){
8648     Roo.Editor.superclass.constructor.call(this, config);
8649     this.field = field;
8650     this.addEvents({
8651         /**
8652              * @event beforestartedit
8653              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8654              * false from the handler of this event.
8655              * @param {Editor} this
8656              * @param {Roo.Element} boundEl The underlying element bound to this editor
8657              * @param {Mixed} value The field value being set
8658              */
8659         "beforestartedit" : true,
8660         /**
8661              * @event startedit
8662              * Fires when this editor is displayed
8663              * @param {Roo.Element} boundEl The underlying element bound to this editor
8664              * @param {Mixed} value The starting field value
8665              */
8666         "startedit" : true,
8667         /**
8668              * @event beforecomplete
8669              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8670              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8671              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8672              * event will not fire since no edit actually occurred.
8673              * @param {Editor} this
8674              * @param {Mixed} value The current field value
8675              * @param {Mixed} startValue The original field value
8676              */
8677         "beforecomplete" : true,
8678         /**
8679              * @event complete
8680              * Fires after editing is complete and any changed value has been written to the underlying field.
8681              * @param {Editor} this
8682              * @param {Mixed} value The current field value
8683              * @param {Mixed} startValue The original field value
8684              */
8685         "complete" : true,
8686         /**
8687          * @event specialkey
8688          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8689          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8690          * @param {Roo.form.Field} this
8691          * @param {Roo.EventObject} e The event object
8692          */
8693         "specialkey" : true
8694     });
8695 };
8696
8697 Roo.extend(Roo.Editor, Roo.Component, {
8698     /**
8699      * @cfg {Boolean/String} autosize
8700      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8701      * or "height" to adopt the height only (defaults to false)
8702      */
8703     /**
8704      * @cfg {Boolean} revertInvalid
8705      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8706      * validation fails (defaults to true)
8707      */
8708     /**
8709      * @cfg {Boolean} ignoreNoChange
8710      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8711      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8712      * will never be ignored.
8713      */
8714     /**
8715      * @cfg {Boolean} hideEl
8716      * False to keep the bound element visible while the editor is displayed (defaults to true)
8717      */
8718     /**
8719      * @cfg {Mixed} value
8720      * The data value of the underlying field (defaults to "")
8721      */
8722     value : "",
8723     /**
8724      * @cfg {String} alignment
8725      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8726      */
8727     alignment: "c-c?",
8728     /**
8729      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8730      * for bottom-right shadow (defaults to "frame")
8731      */
8732     shadow : "frame",
8733     /**
8734      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8735      */
8736     constrain : false,
8737     /**
8738      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8739      */
8740     completeOnEnter : false,
8741     /**
8742      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8743      */
8744     cancelOnEsc : false,
8745     /**
8746      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8747      */
8748     updateEl : false,
8749
8750     // private
8751     onRender : function(ct, position){
8752         this.el = new Roo.Layer({
8753             shadow: this.shadow,
8754             cls: "x-editor",
8755             parentEl : ct,
8756             shim : this.shim,
8757             shadowOffset:4,
8758             id: this.id,
8759             constrain: this.constrain
8760         });
8761         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8762         if(this.field.msgTarget != 'title'){
8763             this.field.msgTarget = 'qtip';
8764         }
8765         this.field.render(this.el);
8766         if(Roo.isGecko){
8767             this.field.el.dom.setAttribute('autocomplete', 'off');
8768         }
8769         this.field.on("specialkey", this.onSpecialKey, this);
8770         if(this.swallowKeys){
8771             this.field.el.swallowEvent(['keydown','keypress']);
8772         }
8773         this.field.show();
8774         this.field.on("blur", this.onBlur, this);
8775         if(this.field.grow){
8776             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8777         }
8778     },
8779
8780     onSpecialKey : function(field, e)
8781     {
8782         //Roo.log('editor onSpecialKey');
8783         if(this.completeOnEnter && e.getKey() == e.ENTER){
8784             e.stopEvent();
8785             this.completeEdit();
8786             return;
8787         }
8788         // do not fire special key otherwise it might hide close the editor...
8789         if(e.getKey() == e.ENTER){    
8790             return;
8791         }
8792         if(this.cancelOnEsc && e.getKey() == e.ESC){
8793             this.cancelEdit();
8794             return;
8795         } 
8796         this.fireEvent('specialkey', field, e);
8797     
8798     },
8799
8800     /**
8801      * Starts the editing process and shows the editor.
8802      * @param {String/HTMLElement/Element} el The element to edit
8803      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8804       * to the innerHTML of el.
8805      */
8806     startEdit : function(el, value){
8807         if(this.editing){
8808             this.completeEdit();
8809         }
8810         this.boundEl = Roo.get(el);
8811         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8812         if(!this.rendered){
8813             this.render(this.parentEl || document.body);
8814         }
8815         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8816             return;
8817         }
8818         this.startValue = v;
8819         this.field.setValue(v);
8820         if(this.autoSize){
8821             var sz = this.boundEl.getSize();
8822             switch(this.autoSize){
8823                 case "width":
8824                 this.setSize(sz.width,  "");
8825                 break;
8826                 case "height":
8827                 this.setSize("",  sz.height);
8828                 break;
8829                 default:
8830                 this.setSize(sz.width,  sz.height);
8831             }
8832         }
8833         this.el.alignTo(this.boundEl, this.alignment);
8834         this.editing = true;
8835         if(Roo.QuickTips){
8836             Roo.QuickTips.disable();
8837         }
8838         this.show();
8839     },
8840
8841     /**
8842      * Sets the height and width of this editor.
8843      * @param {Number} width The new width
8844      * @param {Number} height The new height
8845      */
8846     setSize : function(w, h){
8847         this.field.setSize(w, h);
8848         if(this.el){
8849             this.el.sync();
8850         }
8851     },
8852
8853     /**
8854      * Realigns the editor to the bound field based on the current alignment config value.
8855      */
8856     realign : function(){
8857         this.el.alignTo(this.boundEl, this.alignment);
8858     },
8859
8860     /**
8861      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8862      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8863      */
8864     completeEdit : function(remainVisible){
8865         if(!this.editing){
8866             return;
8867         }
8868         var v = this.getValue();
8869         if(this.revertInvalid !== false && !this.field.isValid()){
8870             v = this.startValue;
8871             this.cancelEdit(true);
8872         }
8873         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8874             this.editing = false;
8875             this.hide();
8876             return;
8877         }
8878         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8879             this.editing = false;
8880             if(this.updateEl && this.boundEl){
8881                 this.boundEl.update(v);
8882             }
8883             if(remainVisible !== true){
8884                 this.hide();
8885             }
8886             this.fireEvent("complete", this, v, this.startValue);
8887         }
8888     },
8889
8890     // private
8891     onShow : function(){
8892         this.el.show();
8893         if(this.hideEl !== false){
8894             this.boundEl.hide();
8895         }
8896         this.field.show();
8897         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8898             this.fixIEFocus = true;
8899             this.deferredFocus.defer(50, this);
8900         }else{
8901             this.field.focus();
8902         }
8903         this.fireEvent("startedit", this.boundEl, this.startValue);
8904     },
8905
8906     deferredFocus : function(){
8907         if(this.editing){
8908             this.field.focus();
8909         }
8910     },
8911
8912     /**
8913      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8914      * reverted to the original starting value.
8915      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8916      * cancel (defaults to false)
8917      */
8918     cancelEdit : function(remainVisible){
8919         if(this.editing){
8920             this.setValue(this.startValue);
8921             if(remainVisible !== true){
8922                 this.hide();
8923             }
8924         }
8925     },
8926
8927     // private
8928     onBlur : function(){
8929         if(this.allowBlur !== true && this.editing){
8930             this.completeEdit();
8931         }
8932     },
8933
8934     // private
8935     onHide : function(){
8936         if(this.editing){
8937             this.completeEdit();
8938             return;
8939         }
8940         this.field.blur();
8941         if(this.field.collapse){
8942             this.field.collapse();
8943         }
8944         this.el.hide();
8945         if(this.hideEl !== false){
8946             this.boundEl.show();
8947         }
8948         if(Roo.QuickTips){
8949             Roo.QuickTips.enable();
8950         }
8951     },
8952
8953     /**
8954      * Sets the data value of the editor
8955      * @param {Mixed} value Any valid value supported by the underlying field
8956      */
8957     setValue : function(v){
8958         this.field.setValue(v);
8959     },
8960
8961     /**
8962      * Gets the data value of the editor
8963      * @return {Mixed} The data value
8964      */
8965     getValue : function(){
8966         return this.field.getValue();
8967     }
8968 });/*
8969  * Based on:
8970  * Ext JS Library 1.1.1
8971  * Copyright(c) 2006-2007, Ext JS, LLC.
8972  *
8973  * Originally Released Under LGPL - original licence link has changed is not relivant.
8974  *
8975  * Fork - LGPL
8976  * <script type="text/javascript">
8977  */
8978  
8979 /**
8980  * @class Roo.BasicDialog
8981  * @extends Roo.util.Observable
8982  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8983  * <pre><code>
8984 var dlg = new Roo.BasicDialog("my-dlg", {
8985     height: 200,
8986     width: 300,
8987     minHeight: 100,
8988     minWidth: 150,
8989     modal: true,
8990     proxyDrag: true,
8991     shadow: true
8992 });
8993 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8994 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
8995 dlg.addButton('Cancel', dlg.hide, dlg);
8996 dlg.show();
8997 </code></pre>
8998   <b>A Dialog should always be a direct child of the body element.</b>
8999  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9000  * @cfg {String} title Default text to display in the title bar (defaults to null)
9001  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9002  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9003  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9004  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9005  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9006  * (defaults to null with no animation)
9007  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9008  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9009  * property for valid values (defaults to 'all')
9010  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9011  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9012  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9013  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9014  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9015  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9016  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9017  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9018  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9019  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9020  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9021  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9022  * draggable = true (defaults to false)
9023  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9024  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9025  * shadow (defaults to false)
9026  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9027  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9028  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9029  * @cfg {Array} buttons Array of buttons
9030  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9031  * @constructor
9032  * Create a new BasicDialog.
9033  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9034  * @param {Object} config Configuration options
9035  */
9036 Roo.BasicDialog = function(el, config){
9037     this.el = Roo.get(el);
9038     var dh = Roo.DomHelper;
9039     if(!this.el && config && config.autoCreate){
9040         if(typeof config.autoCreate == "object"){
9041             if(!config.autoCreate.id){
9042                 config.autoCreate.id = el;
9043             }
9044             this.el = dh.append(document.body,
9045                         config.autoCreate, true);
9046         }else{
9047             this.el = dh.append(document.body,
9048                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9049         }
9050     }
9051     el = this.el;
9052     el.setDisplayed(true);
9053     el.hide = this.hideAction;
9054     this.id = el.id;
9055     el.addClass("x-dlg");
9056
9057     Roo.apply(this, config);
9058
9059     this.proxy = el.createProxy("x-dlg-proxy");
9060     this.proxy.hide = this.hideAction;
9061     this.proxy.setOpacity(.5);
9062     this.proxy.hide();
9063
9064     if(config.width){
9065         el.setWidth(config.width);
9066     }
9067     if(config.height){
9068         el.setHeight(config.height);
9069     }
9070     this.size = el.getSize();
9071     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9072         this.xy = [config.x,config.y];
9073     }else{
9074         this.xy = el.getCenterXY(true);
9075     }
9076     /** The header element @type Roo.Element */
9077     this.header = el.child("> .x-dlg-hd");
9078     /** The body element @type Roo.Element */
9079     this.body = el.child("> .x-dlg-bd");
9080     /** The footer element @type Roo.Element */
9081     this.footer = el.child("> .x-dlg-ft");
9082
9083     if(!this.header){
9084         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9085     }
9086     if(!this.body){
9087         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9088     }
9089
9090     this.header.unselectable();
9091     if(this.title){
9092         this.header.update(this.title);
9093     }
9094     // this element allows the dialog to be focused for keyboard event
9095     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9096     this.focusEl.swallowEvent("click", true);
9097
9098     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9099
9100     // wrap the body and footer for special rendering
9101     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9102     if(this.footer){
9103         this.bwrap.dom.appendChild(this.footer.dom);
9104     }
9105
9106     this.bg = this.el.createChild({
9107         tag: "div", cls:"x-dlg-bg",
9108         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9109     });
9110     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9111
9112
9113     if(this.autoScroll !== false && !this.autoTabs){
9114         this.body.setStyle("overflow", "auto");
9115     }
9116
9117     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9118
9119     if(this.closable !== false){
9120         this.el.addClass("x-dlg-closable");
9121         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9122         this.close.on("click", this.closeClick, this);
9123         this.close.addClassOnOver("x-dlg-close-over");
9124     }
9125     if(this.collapsible !== false){
9126         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9127         this.collapseBtn.on("click", this.collapseClick, this);
9128         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9129         this.header.on("dblclick", this.collapseClick, this);
9130     }
9131     if(this.resizable !== false){
9132         this.el.addClass("x-dlg-resizable");
9133         this.resizer = new Roo.Resizable(el, {
9134             minWidth: this.minWidth || 80,
9135             minHeight:this.minHeight || 80,
9136             handles: this.resizeHandles || "all",
9137             pinned: true
9138         });
9139         this.resizer.on("beforeresize", this.beforeResize, this);
9140         this.resizer.on("resize", this.onResize, this);
9141     }
9142     if(this.draggable !== false){
9143         el.addClass("x-dlg-draggable");
9144         if (!this.proxyDrag) {
9145             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9146         }
9147         else {
9148             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9149         }
9150         dd.setHandleElId(this.header.id);
9151         dd.endDrag = this.endMove.createDelegate(this);
9152         dd.startDrag = this.startMove.createDelegate(this);
9153         dd.onDrag = this.onDrag.createDelegate(this);
9154         dd.scroll = false;
9155         this.dd = dd;
9156     }
9157     if(this.modal){
9158         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9159         this.mask.enableDisplayMode("block");
9160         this.mask.hide();
9161         this.el.addClass("x-dlg-modal");
9162     }
9163     if(this.shadow){
9164         this.shadow = new Roo.Shadow({
9165             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9166             offset : this.shadowOffset
9167         });
9168     }else{
9169         this.shadowOffset = 0;
9170     }
9171     if(Roo.useShims && this.shim !== false){
9172         this.shim = this.el.createShim();
9173         this.shim.hide = this.hideAction;
9174         this.shim.hide();
9175     }else{
9176         this.shim = false;
9177     }
9178     if(this.autoTabs){
9179         this.initTabs();
9180     }
9181     if (this.buttons) { 
9182         var bts= this.buttons;
9183         this.buttons = [];
9184         Roo.each(bts, function(b) {
9185             this.addButton(b);
9186         }, this);
9187     }
9188     
9189     
9190     this.addEvents({
9191         /**
9192          * @event keydown
9193          * Fires when a key is pressed
9194          * @param {Roo.BasicDialog} this
9195          * @param {Roo.EventObject} e
9196          */
9197         "keydown" : true,
9198         /**
9199          * @event move
9200          * Fires when this dialog is moved by the user.
9201          * @param {Roo.BasicDialog} this
9202          * @param {Number} x The new page X
9203          * @param {Number} y The new page Y
9204          */
9205         "move" : true,
9206         /**
9207          * @event resize
9208          * Fires when this dialog is resized by the user.
9209          * @param {Roo.BasicDialog} this
9210          * @param {Number} width The new width
9211          * @param {Number} height The new height
9212          */
9213         "resize" : true,
9214         /**
9215          * @event beforehide
9216          * Fires before this dialog is hidden.
9217          * @param {Roo.BasicDialog} this
9218          */
9219         "beforehide" : true,
9220         /**
9221          * @event hide
9222          * Fires when this dialog is hidden.
9223          * @param {Roo.BasicDialog} this
9224          */
9225         "hide" : true,
9226         /**
9227          * @event beforeshow
9228          * Fires before this dialog is shown.
9229          * @param {Roo.BasicDialog} this
9230          */
9231         "beforeshow" : true,
9232         /**
9233          * @event show
9234          * Fires when this dialog is shown.
9235          * @param {Roo.BasicDialog} this
9236          */
9237         "show" : true
9238     });
9239     el.on("keydown", this.onKeyDown, this);
9240     el.on("mousedown", this.toFront, this);
9241     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9242     this.el.hide();
9243     Roo.DialogManager.register(this);
9244     Roo.BasicDialog.superclass.constructor.call(this);
9245 };
9246
9247 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9248     shadowOffset: Roo.isIE ? 6 : 5,
9249     minHeight: 80,
9250     minWidth: 200,
9251     minButtonWidth: 75,
9252     defaultButton: null,
9253     buttonAlign: "right",
9254     tabTag: 'div',
9255     firstShow: true,
9256
9257     /**
9258      * Sets the dialog title text
9259      * @param {String} text The title text to display
9260      * @return {Roo.BasicDialog} this
9261      */
9262     setTitle : function(text){
9263         this.header.update(text);
9264         return this;
9265     },
9266
9267     // private
9268     closeClick : function(){
9269         this.hide();
9270     },
9271
9272     // private
9273     collapseClick : function(){
9274         this[this.collapsed ? "expand" : "collapse"]();
9275     },
9276
9277     /**
9278      * Collapses the dialog to its minimized state (only the title bar is visible).
9279      * Equivalent to the user clicking the collapse dialog button.
9280      */
9281     collapse : function(){
9282         if(!this.collapsed){
9283             this.collapsed = true;
9284             this.el.addClass("x-dlg-collapsed");
9285             this.restoreHeight = this.el.getHeight();
9286             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9287         }
9288     },
9289
9290     /**
9291      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9292      * clicking the expand dialog button.
9293      */
9294     expand : function(){
9295         if(this.collapsed){
9296             this.collapsed = false;
9297             this.el.removeClass("x-dlg-collapsed");
9298             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9299         }
9300     },
9301
9302     /**
9303      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9304      * @return {Roo.TabPanel} The tabs component
9305      */
9306     initTabs : function(){
9307         var tabs = this.getTabs();
9308         while(tabs.getTab(0)){
9309             tabs.removeTab(0);
9310         }
9311         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9312             var dom = el.dom;
9313             tabs.addTab(Roo.id(dom), dom.title);
9314             dom.title = "";
9315         });
9316         tabs.activate(0);
9317         return tabs;
9318     },
9319
9320     // private
9321     beforeResize : function(){
9322         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9323     },
9324
9325     // private
9326     onResize : function(){
9327         this.refreshSize();
9328         this.syncBodyHeight();
9329         this.adjustAssets();
9330         this.focus();
9331         this.fireEvent("resize", this, this.size.width, this.size.height);
9332     },
9333
9334     // private
9335     onKeyDown : function(e){
9336         if(this.isVisible()){
9337             this.fireEvent("keydown", this, e);
9338         }
9339     },
9340
9341     /**
9342      * Resizes the dialog.
9343      * @param {Number} width
9344      * @param {Number} height
9345      * @return {Roo.BasicDialog} this
9346      */
9347     resizeTo : function(width, height){
9348         this.el.setSize(width, height);
9349         this.size = {width: width, height: height};
9350         this.syncBodyHeight();
9351         if(this.fixedcenter){
9352             this.center();
9353         }
9354         if(this.isVisible()){
9355             this.constrainXY();
9356             this.adjustAssets();
9357         }
9358         this.fireEvent("resize", this, width, height);
9359         return this;
9360     },
9361
9362
9363     /**
9364      * Resizes the dialog to fit the specified content size.
9365      * @param {Number} width
9366      * @param {Number} height
9367      * @return {Roo.BasicDialog} this
9368      */
9369     setContentSize : function(w, h){
9370         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9371         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9372         //if(!this.el.isBorderBox()){
9373             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9374             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9375         //}
9376         if(this.tabs){
9377             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9378             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9379         }
9380         this.resizeTo(w, h);
9381         return this;
9382     },
9383
9384     /**
9385      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9386      * executed in response to a particular key being pressed while the dialog is active.
9387      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9388      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9389      * @param {Function} fn The function to call
9390      * @param {Object} scope (optional) The scope of the function
9391      * @return {Roo.BasicDialog} this
9392      */
9393     addKeyListener : function(key, fn, scope){
9394         var keyCode, shift, ctrl, alt;
9395         if(typeof key == "object" && !(key instanceof Array)){
9396             keyCode = key["key"];
9397             shift = key["shift"];
9398             ctrl = key["ctrl"];
9399             alt = key["alt"];
9400         }else{
9401             keyCode = key;
9402         }
9403         var handler = function(dlg, e){
9404             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9405                 var k = e.getKey();
9406                 if(keyCode instanceof Array){
9407                     for(var i = 0, len = keyCode.length; i < len; i++){
9408                         if(keyCode[i] == k){
9409                           fn.call(scope || window, dlg, k, e);
9410                           return;
9411                         }
9412                     }
9413                 }else{
9414                     if(k == keyCode){
9415                         fn.call(scope || window, dlg, k, e);
9416                     }
9417                 }
9418             }
9419         };
9420         this.on("keydown", handler);
9421         return this;
9422     },
9423
9424     /**
9425      * Returns the TabPanel component (creates it if it doesn't exist).
9426      * Note: If you wish to simply check for the existence of tabs without creating them,
9427      * check for a null 'tabs' property.
9428      * @return {Roo.TabPanel} The tabs component
9429      */
9430     getTabs : function(){
9431         if(!this.tabs){
9432             this.el.addClass("x-dlg-auto-tabs");
9433             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9434             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9435         }
9436         return this.tabs;
9437     },
9438
9439     /**
9440      * Adds a button to the footer section of the dialog.
9441      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9442      * object or a valid Roo.DomHelper element config
9443      * @param {Function} handler The function called when the button is clicked
9444      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9445      * @return {Roo.Button} The new button
9446      */
9447     addButton : function(config, handler, scope){
9448         var dh = Roo.DomHelper;
9449         if(!this.footer){
9450             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9451         }
9452         if(!this.btnContainer){
9453             var tb = this.footer.createChild({
9454
9455                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9456                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9457             }, null, true);
9458             this.btnContainer = tb.firstChild.firstChild.firstChild;
9459         }
9460         var bconfig = {
9461             handler: handler,
9462             scope: scope,
9463             minWidth: this.minButtonWidth,
9464             hideParent:true
9465         };
9466         if(typeof config == "string"){
9467             bconfig.text = config;
9468         }else{
9469             if(config.tag){
9470                 bconfig.dhconfig = config;
9471             }else{
9472                 Roo.apply(bconfig, config);
9473             }
9474         }
9475         var fc = false;
9476         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9477             bconfig.position = Math.max(0, bconfig.position);
9478             fc = this.btnContainer.childNodes[bconfig.position];
9479         }
9480          
9481         var btn = new Roo.Button(
9482             fc ? 
9483                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9484                 : this.btnContainer.appendChild(document.createElement("td")),
9485             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9486             bconfig
9487         );
9488         this.syncBodyHeight();
9489         if(!this.buttons){
9490             /**
9491              * Array of all the buttons that have been added to this dialog via addButton
9492              * @type Array
9493              */
9494             this.buttons = [];
9495         }
9496         this.buttons.push(btn);
9497         return btn;
9498     },
9499
9500     /**
9501      * Sets the default button to be focused when the dialog is displayed.
9502      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9503      * @return {Roo.BasicDialog} this
9504      */
9505     setDefaultButton : function(btn){
9506         this.defaultButton = btn;
9507         return this;
9508     },
9509
9510     // private
9511     getHeaderFooterHeight : function(safe){
9512         var height = 0;
9513         if(this.header){
9514            height += this.header.getHeight();
9515         }
9516         if(this.footer){
9517            var fm = this.footer.getMargins();
9518             height += (this.footer.getHeight()+fm.top+fm.bottom);
9519         }
9520         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9521         height += this.centerBg.getPadding("tb");
9522         return height;
9523     },
9524
9525     // private
9526     syncBodyHeight : function()
9527     {
9528         var bd = this.body, // the text
9529             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9530             bw = this.bwrap;
9531         var height = this.size.height - this.getHeaderFooterHeight(false);
9532         bd.setHeight(height-bd.getMargins("tb"));
9533         var hh = this.header.getHeight();
9534         var h = this.size.height-hh;
9535         cb.setHeight(h);
9536         
9537         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9538         bw.setHeight(h-cb.getPadding("tb"));
9539         
9540         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9541         bd.setWidth(bw.getWidth(true));
9542         if(this.tabs){
9543             this.tabs.syncHeight();
9544             if(Roo.isIE){
9545                 this.tabs.el.repaint();
9546             }
9547         }
9548     },
9549
9550     /**
9551      * Restores the previous state of the dialog if Roo.state is configured.
9552      * @return {Roo.BasicDialog} this
9553      */
9554     restoreState : function(){
9555         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9556         if(box && box.width){
9557             this.xy = [box.x, box.y];
9558             this.resizeTo(box.width, box.height);
9559         }
9560         return this;
9561     },
9562
9563     // private
9564     beforeShow : function(){
9565         this.expand();
9566         if(this.fixedcenter){
9567             this.xy = this.el.getCenterXY(true);
9568         }
9569         if(this.modal){
9570             Roo.get(document.body).addClass("x-body-masked");
9571             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9572             this.mask.show();
9573         }
9574         this.constrainXY();
9575     },
9576
9577     // private
9578     animShow : function(){
9579         var b = Roo.get(this.animateTarget).getBox();
9580         this.proxy.setSize(b.width, b.height);
9581         this.proxy.setLocation(b.x, b.y);
9582         this.proxy.show();
9583         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9584                     true, .35, this.showEl.createDelegate(this));
9585     },
9586
9587     /**
9588      * Shows the dialog.
9589      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9590      * @return {Roo.BasicDialog} this
9591      */
9592     show : function(animateTarget){
9593         if (this.fireEvent("beforeshow", this) === false){
9594             return;
9595         }
9596         if(this.syncHeightBeforeShow){
9597             this.syncBodyHeight();
9598         }else if(this.firstShow){
9599             this.firstShow = false;
9600             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9601         }
9602         this.animateTarget = animateTarget || this.animateTarget;
9603         if(!this.el.isVisible()){
9604             this.beforeShow();
9605             if(this.animateTarget && Roo.get(this.animateTarget)){
9606                 this.animShow();
9607             }else{
9608                 this.showEl();
9609             }
9610         }
9611         return this;
9612     },
9613
9614     // private
9615     showEl : function(){
9616         this.proxy.hide();
9617         this.el.setXY(this.xy);
9618         this.el.show();
9619         this.adjustAssets(true);
9620         this.toFront();
9621         this.focus();
9622         // IE peekaboo bug - fix found by Dave Fenwick
9623         if(Roo.isIE){
9624             this.el.repaint();
9625         }
9626         this.fireEvent("show", this);
9627     },
9628
9629     /**
9630      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9631      * dialog itself will receive focus.
9632      */
9633     focus : function(){
9634         if(this.defaultButton){
9635             this.defaultButton.focus();
9636         }else{
9637             this.focusEl.focus();
9638         }
9639     },
9640
9641     // private
9642     constrainXY : function(){
9643         if(this.constraintoviewport !== false){
9644             if(!this.viewSize){
9645                 if(this.container){
9646                     var s = this.container.getSize();
9647                     this.viewSize = [s.width, s.height];
9648                 }else{
9649                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9650                 }
9651             }
9652             var s = Roo.get(this.container||document).getScroll();
9653
9654             var x = this.xy[0], y = this.xy[1];
9655             var w = this.size.width, h = this.size.height;
9656             var vw = this.viewSize[0], vh = this.viewSize[1];
9657             // only move it if it needs it
9658             var moved = false;
9659             // first validate right/bottom
9660             if(x + w > vw+s.left){
9661                 x = vw - w;
9662                 moved = true;
9663             }
9664             if(y + h > vh+s.top){
9665                 y = vh - h;
9666                 moved = true;
9667             }
9668             // then make sure top/left isn't negative
9669             if(x < s.left){
9670                 x = s.left;
9671                 moved = true;
9672             }
9673             if(y < s.top){
9674                 y = s.top;
9675                 moved = true;
9676             }
9677             if(moved){
9678                 // cache xy
9679                 this.xy = [x, y];
9680                 if(this.isVisible()){
9681                     this.el.setLocation(x, y);
9682                     this.adjustAssets();
9683                 }
9684             }
9685         }
9686     },
9687
9688     // private
9689     onDrag : function(){
9690         if(!this.proxyDrag){
9691             this.xy = this.el.getXY();
9692             this.adjustAssets();
9693         }
9694     },
9695
9696     // private
9697     adjustAssets : function(doShow){
9698         var x = this.xy[0], y = this.xy[1];
9699         var w = this.size.width, h = this.size.height;
9700         if(doShow === true){
9701             if(this.shadow){
9702                 this.shadow.show(this.el);
9703             }
9704             if(this.shim){
9705                 this.shim.show();
9706             }
9707         }
9708         if(this.shadow && this.shadow.isVisible()){
9709             this.shadow.show(this.el);
9710         }
9711         if(this.shim && this.shim.isVisible()){
9712             this.shim.setBounds(x, y, w, h);
9713         }
9714     },
9715
9716     // private
9717     adjustViewport : function(w, h){
9718         if(!w || !h){
9719             w = Roo.lib.Dom.getViewWidth();
9720             h = Roo.lib.Dom.getViewHeight();
9721         }
9722         // cache the size
9723         this.viewSize = [w, h];
9724         if(this.modal && this.mask.isVisible()){
9725             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9726             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9727         }
9728         if(this.isVisible()){
9729             this.constrainXY();
9730         }
9731     },
9732
9733     /**
9734      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9735      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9736      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9737      */
9738     destroy : function(removeEl){
9739         if(this.isVisible()){
9740             this.animateTarget = null;
9741             this.hide();
9742         }
9743         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9744         if(this.tabs){
9745             this.tabs.destroy(removeEl);
9746         }
9747         Roo.destroy(
9748              this.shim,
9749              this.proxy,
9750              this.resizer,
9751              this.close,
9752              this.mask
9753         );
9754         if(this.dd){
9755             this.dd.unreg();
9756         }
9757         if(this.buttons){
9758            for(var i = 0, len = this.buttons.length; i < len; i++){
9759                this.buttons[i].destroy();
9760            }
9761         }
9762         this.el.removeAllListeners();
9763         if(removeEl === true){
9764             this.el.update("");
9765             this.el.remove();
9766         }
9767         Roo.DialogManager.unregister(this);
9768     },
9769
9770     // private
9771     startMove : function(){
9772         if(this.proxyDrag){
9773             this.proxy.show();
9774         }
9775         if(this.constraintoviewport !== false){
9776             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9777         }
9778     },
9779
9780     // private
9781     endMove : function(){
9782         if(!this.proxyDrag){
9783             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9784         }else{
9785             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9786             this.proxy.hide();
9787         }
9788         this.refreshSize();
9789         this.adjustAssets();
9790         this.focus();
9791         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9792     },
9793
9794     /**
9795      * Brings this dialog to the front of any other visible dialogs
9796      * @return {Roo.BasicDialog} this
9797      */
9798     toFront : function(){
9799         Roo.DialogManager.bringToFront(this);
9800         return this;
9801     },
9802
9803     /**
9804      * Sends this dialog to the back (under) of any other visible dialogs
9805      * @return {Roo.BasicDialog} this
9806      */
9807     toBack : function(){
9808         Roo.DialogManager.sendToBack(this);
9809         return this;
9810     },
9811
9812     /**
9813      * Centers this dialog in the viewport
9814      * @return {Roo.BasicDialog} this
9815      */
9816     center : function(){
9817         var xy = this.el.getCenterXY(true);
9818         this.moveTo(xy[0], xy[1]);
9819         return this;
9820     },
9821
9822     /**
9823      * Moves the dialog's top-left corner to the specified point
9824      * @param {Number} x
9825      * @param {Number} y
9826      * @return {Roo.BasicDialog} this
9827      */
9828     moveTo : function(x, y){
9829         this.xy = [x,y];
9830         if(this.isVisible()){
9831             this.el.setXY(this.xy);
9832             this.adjustAssets();
9833         }
9834         return this;
9835     },
9836
9837     /**
9838      * Aligns the dialog to the specified element
9839      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9840      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9841      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9842      * @return {Roo.BasicDialog} this
9843      */
9844     alignTo : function(element, position, offsets){
9845         this.xy = this.el.getAlignToXY(element, position, offsets);
9846         if(this.isVisible()){
9847             this.el.setXY(this.xy);
9848             this.adjustAssets();
9849         }
9850         return this;
9851     },
9852
9853     /**
9854      * Anchors an element to another element and realigns it when the window is resized.
9855      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9856      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9857      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9858      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9859      * is a number, it is used as the buffer delay (defaults to 50ms).
9860      * @return {Roo.BasicDialog} this
9861      */
9862     anchorTo : function(el, alignment, offsets, monitorScroll){
9863         var action = function(){
9864             this.alignTo(el, alignment, offsets);
9865         };
9866         Roo.EventManager.onWindowResize(action, this);
9867         var tm = typeof monitorScroll;
9868         if(tm != 'undefined'){
9869             Roo.EventManager.on(window, 'scroll', action, this,
9870                 {buffer: tm == 'number' ? monitorScroll : 50});
9871         }
9872         action.call(this);
9873         return this;
9874     },
9875
9876     /**
9877      * Returns true if the dialog is visible
9878      * @return {Boolean}
9879      */
9880     isVisible : function(){
9881         return this.el.isVisible();
9882     },
9883
9884     // private
9885     animHide : function(callback){
9886         var b = Roo.get(this.animateTarget).getBox();
9887         this.proxy.show();
9888         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9889         this.el.hide();
9890         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9891                     this.hideEl.createDelegate(this, [callback]));
9892     },
9893
9894     /**
9895      * Hides the dialog.
9896      * @param {Function} callback (optional) Function to call when the dialog is hidden
9897      * @return {Roo.BasicDialog} this
9898      */
9899     hide : function(callback){
9900         if (this.fireEvent("beforehide", this) === false){
9901             return;
9902         }
9903         if(this.shadow){
9904             this.shadow.hide();
9905         }
9906         if(this.shim) {
9907           this.shim.hide();
9908         }
9909         // sometimes animateTarget seems to get set.. causing problems...
9910         // this just double checks..
9911         if(this.animateTarget && Roo.get(this.animateTarget)) {
9912            this.animHide(callback);
9913         }else{
9914             this.el.hide();
9915             this.hideEl(callback);
9916         }
9917         return this;
9918     },
9919
9920     // private
9921     hideEl : function(callback){
9922         this.proxy.hide();
9923         if(this.modal){
9924             this.mask.hide();
9925             Roo.get(document.body).removeClass("x-body-masked");
9926         }
9927         this.fireEvent("hide", this);
9928         if(typeof callback == "function"){
9929             callback();
9930         }
9931     },
9932
9933     // private
9934     hideAction : function(){
9935         this.setLeft("-10000px");
9936         this.setTop("-10000px");
9937         this.setStyle("visibility", "hidden");
9938     },
9939
9940     // private
9941     refreshSize : function(){
9942         this.size = this.el.getSize();
9943         this.xy = this.el.getXY();
9944         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9945     },
9946
9947     // private
9948     // z-index is managed by the DialogManager and may be overwritten at any time
9949     setZIndex : function(index){
9950         if(this.modal){
9951             this.mask.setStyle("z-index", index);
9952         }
9953         if(this.shim){
9954             this.shim.setStyle("z-index", ++index);
9955         }
9956         if(this.shadow){
9957             this.shadow.setZIndex(++index);
9958         }
9959         this.el.setStyle("z-index", ++index);
9960         if(this.proxy){
9961             this.proxy.setStyle("z-index", ++index);
9962         }
9963         if(this.resizer){
9964             this.resizer.proxy.setStyle("z-index", ++index);
9965         }
9966
9967         this.lastZIndex = index;
9968     },
9969
9970     /**
9971      * Returns the element for this dialog
9972      * @return {Roo.Element} The underlying dialog Element
9973      */
9974     getEl : function(){
9975         return this.el;
9976     }
9977 });
9978
9979 /**
9980  * @class Roo.DialogManager
9981  * Provides global access to BasicDialogs that have been created and
9982  * support for z-indexing (layering) multiple open dialogs.
9983  */
9984 Roo.DialogManager = function(){
9985     var list = {};
9986     var accessList = [];
9987     var front = null;
9988
9989     // private
9990     var sortDialogs = function(d1, d2){
9991         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9992     };
9993
9994     // private
9995     var orderDialogs = function(){
9996         accessList.sort(sortDialogs);
9997         var seed = Roo.DialogManager.zseed;
9998         for(var i = 0, len = accessList.length; i < len; i++){
9999             var dlg = accessList[i];
10000             if(dlg){
10001                 dlg.setZIndex(seed + (i*10));
10002             }
10003         }
10004     };
10005
10006     return {
10007         /**
10008          * The starting z-index for BasicDialogs (defaults to 9000)
10009          * @type Number The z-index value
10010          */
10011         zseed : 9000,
10012
10013         // private
10014         register : function(dlg){
10015             list[dlg.id] = dlg;
10016             accessList.push(dlg);
10017         },
10018
10019         // private
10020         unregister : function(dlg){
10021             delete list[dlg.id];
10022             var i=0;
10023             var len=0;
10024             if(!accessList.indexOf){
10025                 for(  i = 0, len = accessList.length; i < len; i++){
10026                     if(accessList[i] == dlg){
10027                         accessList.splice(i, 1);
10028                         return;
10029                     }
10030                 }
10031             }else{
10032                  i = accessList.indexOf(dlg);
10033                 if(i != -1){
10034                     accessList.splice(i, 1);
10035                 }
10036             }
10037         },
10038
10039         /**
10040          * Gets a registered dialog by id
10041          * @param {String/Object} id The id of the dialog or a dialog
10042          * @return {Roo.BasicDialog} this
10043          */
10044         get : function(id){
10045             return typeof id == "object" ? id : list[id];
10046         },
10047
10048         /**
10049          * Brings the specified dialog to the front
10050          * @param {String/Object} dlg The id of the dialog or a dialog
10051          * @return {Roo.BasicDialog} this
10052          */
10053         bringToFront : function(dlg){
10054             dlg = this.get(dlg);
10055             if(dlg != front){
10056                 front = dlg;
10057                 dlg._lastAccess = new Date().getTime();
10058                 orderDialogs();
10059             }
10060             return dlg;
10061         },
10062
10063         /**
10064          * Sends the specified dialog to the back
10065          * @param {String/Object} dlg The id of the dialog or a dialog
10066          * @return {Roo.BasicDialog} this
10067          */
10068         sendToBack : function(dlg){
10069             dlg = this.get(dlg);
10070             dlg._lastAccess = -(new Date().getTime());
10071             orderDialogs();
10072             return dlg;
10073         },
10074
10075         /**
10076          * Hides all dialogs
10077          */
10078         hideAll : function(){
10079             for(var id in list){
10080                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10081                     list[id].hide();
10082                 }
10083             }
10084         }
10085     };
10086 }();
10087
10088 /**
10089  * @class Roo.LayoutDialog
10090  * @extends Roo.BasicDialog
10091  * Dialog which provides adjustments for working with a layout in a Dialog.
10092  * Add your necessary layout config options to the dialog's config.<br>
10093  * Example usage (including a nested layout):
10094  * <pre><code>
10095 if(!dialog){
10096     dialog = new Roo.LayoutDialog("download-dlg", {
10097         modal: true,
10098         width:600,
10099         height:450,
10100         shadow:true,
10101         minWidth:500,
10102         minHeight:350,
10103         autoTabs:true,
10104         proxyDrag:true,
10105         // layout config merges with the dialog config
10106         center:{
10107             tabPosition: "top",
10108             alwaysShowTabs: true
10109         }
10110     });
10111     dialog.addKeyListener(27, dialog.hide, dialog);
10112     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10113     dialog.addButton("Build It!", this.getDownload, this);
10114
10115     // we can even add nested layouts
10116     var innerLayout = new Roo.BorderLayout("dl-inner", {
10117         east: {
10118             initialSize: 200,
10119             autoScroll:true,
10120             split:true
10121         },
10122         center: {
10123             autoScroll:true
10124         }
10125     });
10126     innerLayout.beginUpdate();
10127     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10128     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10129     innerLayout.endUpdate(true);
10130
10131     var layout = dialog.getLayout();
10132     layout.beginUpdate();
10133     layout.add("center", new Roo.ContentPanel("standard-panel",
10134                         {title: "Download the Source", fitToFrame:true}));
10135     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10136                {title: "Build your own roo.js"}));
10137     layout.getRegion("center").showPanel(sp);
10138     layout.endUpdate();
10139 }
10140 </code></pre>
10141     * @constructor
10142     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10143     * @param {Object} config configuration options
10144   */
10145 Roo.LayoutDialog = function(el, cfg){
10146     
10147     var config=  cfg;
10148     if (typeof(cfg) == 'undefined') {
10149         config = Roo.apply({}, el);
10150         // not sure why we use documentElement here.. - it should always be body.
10151         // IE7 borks horribly if we use documentElement.
10152         // webkit also does not like documentElement - it creates a body element...
10153         el = Roo.get( document.body || document.documentElement ).createChild();
10154         //config.autoCreate = true;
10155     }
10156     
10157     
10158     config.autoTabs = false;
10159     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10160     this.body.setStyle({overflow:"hidden", position:"relative"});
10161     this.layout = new Roo.BorderLayout(this.body.dom, config);
10162     this.layout.monitorWindowResize = false;
10163     this.el.addClass("x-dlg-auto-layout");
10164     // fix case when center region overwrites center function
10165     this.center = Roo.BasicDialog.prototype.center;
10166     this.on("show", this.layout.layout, this.layout, true);
10167     if (config.items) {
10168         var xitems = config.items;
10169         delete config.items;
10170         Roo.each(xitems, this.addxtype, this);
10171     }
10172     
10173     
10174 };
10175 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10176     /**
10177      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10178      * @deprecated
10179      */
10180     endUpdate : function(){
10181         this.layout.endUpdate();
10182     },
10183
10184     /**
10185      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10186      *  @deprecated
10187      */
10188     beginUpdate : function(){
10189         this.layout.beginUpdate();
10190     },
10191
10192     /**
10193      * Get the BorderLayout for this dialog
10194      * @return {Roo.BorderLayout}
10195      */
10196     getLayout : function(){
10197         return this.layout;
10198     },
10199
10200     showEl : function(){
10201         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10202         if(Roo.isIE7){
10203             this.layout.layout();
10204         }
10205     },
10206
10207     // private
10208     // Use the syncHeightBeforeShow config option to control this automatically
10209     syncBodyHeight : function(){
10210         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10211         if(this.layout){this.layout.layout();}
10212     },
10213     
10214       /**
10215      * Add an xtype element (actually adds to the layout.)
10216      * @return {Object} xdata xtype object data.
10217      */
10218     
10219     addxtype : function(c) {
10220         return this.layout.addxtype(c);
10221     }
10222 });/*
10223  * Based on:
10224  * Ext JS Library 1.1.1
10225  * Copyright(c) 2006-2007, Ext JS, LLC.
10226  *
10227  * Originally Released Under LGPL - original licence link has changed is not relivant.
10228  *
10229  * Fork - LGPL
10230  * <script type="text/javascript">
10231  */
10232  
10233 /**
10234  * @class Roo.MessageBox
10235  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10236  * Example usage:
10237  *<pre><code>
10238 // Basic alert:
10239 Roo.Msg.alert('Status', 'Changes saved successfully.');
10240
10241 // Prompt for user data:
10242 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10243     if (btn == 'ok'){
10244         // process text value...
10245     }
10246 });
10247
10248 // Show a dialog using config options:
10249 Roo.Msg.show({
10250    title:'Save Changes?',
10251    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10252    buttons: Roo.Msg.YESNOCANCEL,
10253    fn: processResult,
10254    animEl: 'elId'
10255 });
10256 </code></pre>
10257  * @singleton
10258  */
10259 Roo.MessageBox = function(){
10260     var dlg, opt, mask, waitTimer;
10261     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10262     var buttons, activeTextEl, bwidth;
10263
10264     // private
10265     var handleButton = function(button){
10266         dlg.hide();
10267         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10268     };
10269
10270     // private
10271     var handleHide = function(){
10272         if(opt && opt.cls){
10273             dlg.el.removeClass(opt.cls);
10274         }
10275         if(waitTimer){
10276             Roo.TaskMgr.stop(waitTimer);
10277             waitTimer = null;
10278         }
10279     };
10280
10281     // private
10282     var updateButtons = function(b){
10283         var width = 0;
10284         if(!b){
10285             buttons["ok"].hide();
10286             buttons["cancel"].hide();
10287             buttons["yes"].hide();
10288             buttons["no"].hide();
10289             dlg.footer.dom.style.display = 'none';
10290             return width;
10291         }
10292         dlg.footer.dom.style.display = '';
10293         for(var k in buttons){
10294             if(typeof buttons[k] != "function"){
10295                 if(b[k]){
10296                     buttons[k].show();
10297                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10298                     width += buttons[k].el.getWidth()+15;
10299                 }else{
10300                     buttons[k].hide();
10301                 }
10302             }
10303         }
10304         return width;
10305     };
10306
10307     // private
10308     var handleEsc = function(d, k, e){
10309         if(opt && opt.closable !== false){
10310             dlg.hide();
10311         }
10312         if(e){
10313             e.stopEvent();
10314         }
10315     };
10316
10317     return {
10318         /**
10319          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10320          * @return {Roo.BasicDialog} The BasicDialog element
10321          */
10322         getDialog : function(){
10323            if(!dlg){
10324                 dlg = new Roo.BasicDialog("x-msg-box", {
10325                     autoCreate : true,
10326                     shadow: true,
10327                     draggable: true,
10328                     resizable:false,
10329                     constraintoviewport:false,
10330                     fixedcenter:true,
10331                     collapsible : false,
10332                     shim:true,
10333                     modal: true,
10334                     width:400, height:100,
10335                     buttonAlign:"center",
10336                     closeClick : function(){
10337                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10338                             handleButton("no");
10339                         }else{
10340                             handleButton("cancel");
10341                         }
10342                     }
10343                 });
10344                 dlg.on("hide", handleHide);
10345                 mask = dlg.mask;
10346                 dlg.addKeyListener(27, handleEsc);
10347                 buttons = {};
10348                 var bt = this.buttonText;
10349                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10350                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10351                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10352                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10353                 bodyEl = dlg.body.createChild({
10354
10355                     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>'
10356                 });
10357                 msgEl = bodyEl.dom.firstChild;
10358                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10359                 textboxEl.enableDisplayMode();
10360                 textboxEl.addKeyListener([10,13], function(){
10361                     if(dlg.isVisible() && opt && opt.buttons){
10362                         if(opt.buttons.ok){
10363                             handleButton("ok");
10364                         }else if(opt.buttons.yes){
10365                             handleButton("yes");
10366                         }
10367                     }
10368                 });
10369                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10370                 textareaEl.enableDisplayMode();
10371                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10372                 progressEl.enableDisplayMode();
10373                 var pf = progressEl.dom.firstChild;
10374                 if (pf) {
10375                     pp = Roo.get(pf.firstChild);
10376                     pp.setHeight(pf.offsetHeight);
10377                 }
10378                 
10379             }
10380             return dlg;
10381         },
10382
10383         /**
10384          * Updates the message box body text
10385          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10386          * the XHTML-compliant non-breaking space character '&amp;#160;')
10387          * @return {Roo.MessageBox} This message box
10388          */
10389         updateText : function(text){
10390             if(!dlg.isVisible() && !opt.width){
10391                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10392             }
10393             msgEl.innerHTML = text || '&#160;';
10394       
10395             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10396             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10397             var w = Math.max(
10398                     Math.min(opt.width || cw , this.maxWidth), 
10399                     Math.max(opt.minWidth || this.minWidth, bwidth)
10400             );
10401             if(opt.prompt){
10402                 activeTextEl.setWidth(w);
10403             }
10404             if(dlg.isVisible()){
10405                 dlg.fixedcenter = false;
10406             }
10407             // to big, make it scroll. = But as usual stupid IE does not support
10408             // !important..
10409             
10410             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10411                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10412                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10413             } else {
10414                 bodyEl.dom.style.height = '';
10415                 bodyEl.dom.style.overflowY = '';
10416             }
10417             if (cw > w) {
10418                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10419             } else {
10420                 bodyEl.dom.style.overflowX = '';
10421             }
10422             
10423             dlg.setContentSize(w, bodyEl.getHeight());
10424             if(dlg.isVisible()){
10425                 dlg.fixedcenter = true;
10426             }
10427             return this;
10428         },
10429
10430         /**
10431          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10432          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10433          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10434          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10435          * @return {Roo.MessageBox} This message box
10436          */
10437         updateProgress : function(value, text){
10438             if(text){
10439                 this.updateText(text);
10440             }
10441             if (pp) { // weird bug on my firefox - for some reason this is not defined
10442                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10443             }
10444             return this;
10445         },        
10446
10447         /**
10448          * Returns true if the message box is currently displayed
10449          * @return {Boolean} True if the message box is visible, else false
10450          */
10451         isVisible : function(){
10452             return dlg && dlg.isVisible();  
10453         },
10454
10455         /**
10456          * Hides the message box if it is displayed
10457          */
10458         hide : function(){
10459             if(this.isVisible()){
10460                 dlg.hide();
10461             }  
10462         },
10463
10464         /**
10465          * Displays a new message box, or reinitializes an existing message box, based on the config options
10466          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10467          * The following config object properties are supported:
10468          * <pre>
10469 Property    Type             Description
10470 ----------  ---------------  ------------------------------------------------------------------------------------
10471 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10472                                    closes (defaults to undefined)
10473 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10474                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10475 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10476                                    progress and wait dialogs will ignore this property and always hide the
10477                                    close button as they can only be closed programmatically.
10478 cls               String           A custom CSS class to apply to the message box element
10479 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10480                                    displayed (defaults to 75)
10481 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10482                                    function will be btn (the name of the button that was clicked, if applicable,
10483                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10484                                    Progress and wait dialogs will ignore this option since they do not respond to
10485                                    user actions and can only be closed programmatically, so any required function
10486                                    should be called by the same code after it closes the dialog.
10487 icon              String           A CSS class that provides a background image to be used as an icon for
10488                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10489 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10490 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10491 modal             Boolean          False to allow user interaction with the page while the message box is
10492                                    displayed (defaults to true)
10493 msg               String           A string that will replace the existing message box body text (defaults
10494                                    to the XHTML-compliant non-breaking space character '&#160;')
10495 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10496 progress          Boolean          True to display a progress bar (defaults to false)
10497 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10498 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10499 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10500 title             String           The title text
10501 value             String           The string value to set into the active textbox element if displayed
10502 wait              Boolean          True to display a progress bar (defaults to false)
10503 width             Number           The width of the dialog in pixels
10504 </pre>
10505          *
10506          * Example usage:
10507          * <pre><code>
10508 Roo.Msg.show({
10509    title: 'Address',
10510    msg: 'Please enter your address:',
10511    width: 300,
10512    buttons: Roo.MessageBox.OKCANCEL,
10513    multiline: true,
10514    fn: saveAddress,
10515    animEl: 'addAddressBtn'
10516 });
10517 </code></pre>
10518          * @param {Object} config Configuration options
10519          * @return {Roo.MessageBox} This message box
10520          */
10521         show : function(options)
10522         {
10523             
10524             // this causes nightmares if you show one dialog after another
10525             // especially on callbacks..
10526              
10527             if(this.isVisible()){
10528                 
10529                 this.hide();
10530                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10531                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10532                 Roo.log("New Dialog Message:" +  options.msg )
10533                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10534                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10535                 
10536             }
10537             var d = this.getDialog();
10538             opt = options;
10539             d.setTitle(opt.title || "&#160;");
10540             d.close.setDisplayed(opt.closable !== false);
10541             activeTextEl = textboxEl;
10542             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10543             if(opt.prompt){
10544                 if(opt.multiline){
10545                     textboxEl.hide();
10546                     textareaEl.show();
10547                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10548                         opt.multiline : this.defaultTextHeight);
10549                     activeTextEl = textareaEl;
10550                 }else{
10551                     textboxEl.show();
10552                     textareaEl.hide();
10553                 }
10554             }else{
10555                 textboxEl.hide();
10556                 textareaEl.hide();
10557             }
10558             progressEl.setDisplayed(opt.progress === true);
10559             this.updateProgress(0);
10560             activeTextEl.dom.value = opt.value || "";
10561             if(opt.prompt){
10562                 dlg.setDefaultButton(activeTextEl);
10563             }else{
10564                 var bs = opt.buttons;
10565                 var db = null;
10566                 if(bs && bs.ok){
10567                     db = buttons["ok"];
10568                 }else if(bs && bs.yes){
10569                     db = buttons["yes"];
10570                 }
10571                 dlg.setDefaultButton(db);
10572             }
10573             bwidth = updateButtons(opt.buttons);
10574             this.updateText(opt.msg);
10575             if(opt.cls){
10576                 d.el.addClass(opt.cls);
10577             }
10578             d.proxyDrag = opt.proxyDrag === true;
10579             d.modal = opt.modal !== false;
10580             d.mask = opt.modal !== false ? mask : false;
10581             if(!d.isVisible()){
10582                 // force it to the end of the z-index stack so it gets a cursor in FF
10583                 document.body.appendChild(dlg.el.dom);
10584                 d.animateTarget = null;
10585                 d.show(options.animEl);
10586             }
10587             return this;
10588         },
10589
10590         /**
10591          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10592          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10593          * and closing the message box when the process is complete.
10594          * @param {String} title The title bar text
10595          * @param {String} msg The message box body text
10596          * @return {Roo.MessageBox} This message box
10597          */
10598         progress : function(title, msg){
10599             this.show({
10600                 title : title,
10601                 msg : msg,
10602                 buttons: false,
10603                 progress:true,
10604                 closable:false,
10605                 minWidth: this.minProgressWidth,
10606                 modal : true
10607             });
10608             return this;
10609         },
10610
10611         /**
10612          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10613          * If a callback function is passed it will be called after the user clicks the button, and the
10614          * id of the button that was clicked will be passed as the only parameter to the callback
10615          * (could also be the top-right close button).
10616          * @param {String} title The title bar text
10617          * @param {String} msg The message box body text
10618          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10619          * @param {Object} scope (optional) The scope of the callback function
10620          * @return {Roo.MessageBox} This message box
10621          */
10622         alert : function(title, msg, fn, scope){
10623             this.show({
10624                 title : title,
10625                 msg : msg,
10626                 buttons: this.OK,
10627                 fn: fn,
10628                 scope : scope,
10629                 modal : true
10630             });
10631             return this;
10632         },
10633
10634         /**
10635          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10636          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10637          * You are responsible for closing the message box when the process is complete.
10638          * @param {String} msg The message box body text
10639          * @param {String} title (optional) The title bar text
10640          * @return {Roo.MessageBox} This message box
10641          */
10642         wait : function(msg, title){
10643             this.show({
10644                 title : title,
10645                 msg : msg,
10646                 buttons: false,
10647                 closable:false,
10648                 progress:true,
10649                 modal:true,
10650                 width:300,
10651                 wait:true
10652             });
10653             waitTimer = Roo.TaskMgr.start({
10654                 run: function(i){
10655                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10656                 },
10657                 interval: 1000
10658             });
10659             return this;
10660         },
10661
10662         /**
10663          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10664          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10665          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10666          * @param {String} title The title bar text
10667          * @param {String} msg The message box body text
10668          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10669          * @param {Object} scope (optional) The scope of the callback function
10670          * @return {Roo.MessageBox} This message box
10671          */
10672         confirm : function(title, msg, fn, scope){
10673             this.show({
10674                 title : title,
10675                 msg : msg,
10676                 buttons: this.YESNO,
10677                 fn: fn,
10678                 scope : scope,
10679                 modal : true
10680             });
10681             return this;
10682         },
10683
10684         /**
10685          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10686          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10687          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10688          * (could also be the top-right close button) and the text that was entered will be passed as the two
10689          * parameters to the callback.
10690          * @param {String} title The title bar text
10691          * @param {String} msg The message box body text
10692          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10693          * @param {Object} scope (optional) The scope of the callback function
10694          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10695          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10696          * @return {Roo.MessageBox} This message box
10697          */
10698         prompt : function(title, msg, fn, scope, multiline){
10699             this.show({
10700                 title : title,
10701                 msg : msg,
10702                 buttons: this.OKCANCEL,
10703                 fn: fn,
10704                 minWidth:250,
10705                 scope : scope,
10706                 prompt:true,
10707                 multiline: multiline,
10708                 modal : true
10709             });
10710             return this;
10711         },
10712
10713         /**
10714          * Button config that displays a single OK button
10715          * @type Object
10716          */
10717         OK : {ok:true},
10718         /**
10719          * Button config that displays Yes and No buttons
10720          * @type Object
10721          */
10722         YESNO : {yes:true, no:true},
10723         /**
10724          * Button config that displays OK and Cancel buttons
10725          * @type Object
10726          */
10727         OKCANCEL : {ok:true, cancel:true},
10728         /**
10729          * Button config that displays Yes, No and Cancel buttons
10730          * @type Object
10731          */
10732         YESNOCANCEL : {yes:true, no:true, cancel:true},
10733
10734         /**
10735          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10736          * @type Number
10737          */
10738         defaultTextHeight : 75,
10739         /**
10740          * The maximum width in pixels of the message box (defaults to 600)
10741          * @type Number
10742          */
10743         maxWidth : 600,
10744         /**
10745          * The minimum width in pixels of the message box (defaults to 100)
10746          * @type Number
10747          */
10748         minWidth : 100,
10749         /**
10750          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10751          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10752          * @type Number
10753          */
10754         minProgressWidth : 250,
10755         /**
10756          * An object containing the default button text strings that can be overriden for localized language support.
10757          * Supported properties are: ok, cancel, yes and no.
10758          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10759          * @type Object
10760          */
10761         buttonText : {
10762             ok : "OK",
10763             cancel : "Cancel",
10764             yes : "Yes",
10765             no : "No"
10766         }
10767     };
10768 }();
10769
10770 /**
10771  * Shorthand for {@link Roo.MessageBox}
10772  */
10773 Roo.Msg = Roo.MessageBox;/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783 /**
10784  * @class Roo.QuickTips
10785  * Provides attractive and customizable tooltips for any element.
10786  * @singleton
10787  */
10788 Roo.QuickTips = function(){
10789     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10790     var ce, bd, xy, dd;
10791     var visible = false, disabled = true, inited = false;
10792     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10793     
10794     var onOver = function(e){
10795         if(disabled){
10796             return;
10797         }
10798         var t = e.getTarget();
10799         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10800             return;
10801         }
10802         if(ce && t == ce.el){
10803             clearTimeout(hideProc);
10804             return;
10805         }
10806         if(t && tagEls[t.id]){
10807             tagEls[t.id].el = t;
10808             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10809             return;
10810         }
10811         var ttp, et = Roo.fly(t);
10812         var ns = cfg.namespace;
10813         if(tm.interceptTitles && t.title){
10814             ttp = t.title;
10815             t.qtip = ttp;
10816             t.removeAttribute("title");
10817             e.preventDefault();
10818         }else{
10819             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10820         }
10821         if(ttp){
10822             showProc = show.defer(tm.showDelay, tm, [{
10823                 el: t, 
10824                 text: ttp.replace(/\\n/g,'<br/>'),
10825                 width: et.getAttributeNS(ns, cfg.width),
10826                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10827                 title: et.getAttributeNS(ns, cfg.title),
10828                     cls: et.getAttributeNS(ns, cfg.cls)
10829             }]);
10830         }
10831     };
10832     
10833     var onOut = function(e){
10834         clearTimeout(showProc);
10835         var t = e.getTarget();
10836         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10837             hideProc = setTimeout(hide, tm.hideDelay);
10838         }
10839     };
10840     
10841     var onMove = function(e){
10842         if(disabled){
10843             return;
10844         }
10845         xy = e.getXY();
10846         xy[1] += 18;
10847         if(tm.trackMouse && ce){
10848             el.setXY(xy);
10849         }
10850     };
10851     
10852     var onDown = function(e){
10853         clearTimeout(showProc);
10854         clearTimeout(hideProc);
10855         if(!e.within(el)){
10856             if(tm.hideOnClick){
10857                 hide();
10858                 tm.disable();
10859                 tm.enable.defer(100, tm);
10860             }
10861         }
10862     };
10863     
10864     var getPad = function(){
10865         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10866     };
10867
10868     var show = function(o){
10869         if(disabled){
10870             return;
10871         }
10872         clearTimeout(dismissProc);
10873         ce = o;
10874         if(removeCls){ // in case manually hidden
10875             el.removeClass(removeCls);
10876             removeCls = null;
10877         }
10878         if(ce.cls){
10879             el.addClass(ce.cls);
10880             removeCls = ce.cls;
10881         }
10882         if(ce.title){
10883             tipTitle.update(ce.title);
10884             tipTitle.show();
10885         }else{
10886             tipTitle.update('');
10887             tipTitle.hide();
10888         }
10889         el.dom.style.width  = tm.maxWidth+'px';
10890         //tipBody.dom.style.width = '';
10891         tipBodyText.update(o.text);
10892         var p = getPad(), w = ce.width;
10893         if(!w){
10894             var td = tipBodyText.dom;
10895             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10896             if(aw > tm.maxWidth){
10897                 w = tm.maxWidth;
10898             }else if(aw < tm.minWidth){
10899                 w = tm.minWidth;
10900             }else{
10901                 w = aw;
10902             }
10903         }
10904         //tipBody.setWidth(w);
10905         el.setWidth(parseInt(w, 10) + p);
10906         if(ce.autoHide === false){
10907             close.setDisplayed(true);
10908             if(dd){
10909                 dd.unlock();
10910             }
10911         }else{
10912             close.setDisplayed(false);
10913             if(dd){
10914                 dd.lock();
10915             }
10916         }
10917         if(xy){
10918             el.avoidY = xy[1]-18;
10919             el.setXY(xy);
10920         }
10921         if(tm.animate){
10922             el.setOpacity(.1);
10923             el.setStyle("visibility", "visible");
10924             el.fadeIn({callback: afterShow});
10925         }else{
10926             afterShow();
10927         }
10928     };
10929     
10930     var afterShow = function(){
10931         if(ce){
10932             el.show();
10933             esc.enable();
10934             if(tm.autoDismiss && ce.autoHide !== false){
10935                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10936             }
10937         }
10938     };
10939     
10940     var hide = function(noanim){
10941         clearTimeout(dismissProc);
10942         clearTimeout(hideProc);
10943         ce = null;
10944         if(el.isVisible()){
10945             esc.disable();
10946             if(noanim !== true && tm.animate){
10947                 el.fadeOut({callback: afterHide});
10948             }else{
10949                 afterHide();
10950             } 
10951         }
10952     };
10953     
10954     var afterHide = function(){
10955         el.hide();
10956         if(removeCls){
10957             el.removeClass(removeCls);
10958             removeCls = null;
10959         }
10960     };
10961     
10962     return {
10963         /**
10964         * @cfg {Number} minWidth
10965         * The minimum width of the quick tip (defaults to 40)
10966         */
10967        minWidth : 40,
10968         /**
10969         * @cfg {Number} maxWidth
10970         * The maximum width of the quick tip (defaults to 300)
10971         */
10972        maxWidth : 300,
10973         /**
10974         * @cfg {Boolean} interceptTitles
10975         * True to automatically use the element's DOM title value if available (defaults to false)
10976         */
10977        interceptTitles : false,
10978         /**
10979         * @cfg {Boolean} trackMouse
10980         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
10981         */
10982        trackMouse : false,
10983         /**
10984         * @cfg {Boolean} hideOnClick
10985         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
10986         */
10987        hideOnClick : true,
10988         /**
10989         * @cfg {Number} showDelay
10990         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
10991         */
10992        showDelay : 500,
10993         /**
10994         * @cfg {Number} hideDelay
10995         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
10996         */
10997        hideDelay : 200,
10998         /**
10999         * @cfg {Boolean} autoHide
11000         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11001         * Used in conjunction with hideDelay.
11002         */
11003        autoHide : true,
11004         /**
11005         * @cfg {Boolean}
11006         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11007         * (defaults to true).  Used in conjunction with autoDismissDelay.
11008         */
11009        autoDismiss : true,
11010         /**
11011         * @cfg {Number}
11012         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11013         */
11014        autoDismissDelay : 5000,
11015        /**
11016         * @cfg {Boolean} animate
11017         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11018         */
11019        animate : false,
11020
11021        /**
11022         * @cfg {String} title
11023         * Title text to display (defaults to '').  This can be any valid HTML markup.
11024         */
11025         title: '',
11026        /**
11027         * @cfg {String} text
11028         * Body text to display (defaults to '').  This can be any valid HTML markup.
11029         */
11030         text : '',
11031        /**
11032         * @cfg {String} cls
11033         * A CSS class to apply to the base quick tip element (defaults to '').
11034         */
11035         cls : '',
11036        /**
11037         * @cfg {Number} width
11038         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11039         * minWidth or maxWidth.
11040         */
11041         width : null,
11042
11043     /**
11044      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11045      * or display QuickTips in a page.
11046      */
11047        init : function(){
11048           tm = Roo.QuickTips;
11049           cfg = tm.tagConfig;
11050           if(!inited){
11051               if(!Roo.isReady){ // allow calling of init() before onReady
11052                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11053                   return;
11054               }
11055               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11056               el.fxDefaults = {stopFx: true};
11057               // maximum custom styling
11058               //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>');
11059               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>');              
11060               tipTitle = el.child('h3');
11061               tipTitle.enableDisplayMode("block");
11062               tipBody = el.child('div.x-tip-bd');
11063               tipBodyText = el.child('div.x-tip-bd-inner');
11064               //bdLeft = el.child('div.x-tip-bd-left');
11065               //bdRight = el.child('div.x-tip-bd-right');
11066               close = el.child('div.x-tip-close');
11067               close.enableDisplayMode("block");
11068               close.on("click", hide);
11069               var d = Roo.get(document);
11070               d.on("mousedown", onDown);
11071               d.on("mouseover", onOver);
11072               d.on("mouseout", onOut);
11073               d.on("mousemove", onMove);
11074               esc = d.addKeyListener(27, hide);
11075               esc.disable();
11076               if(Roo.dd.DD){
11077                   dd = el.initDD("default", null, {
11078                       onDrag : function(){
11079                           el.sync();  
11080                       }
11081                   });
11082                   dd.setHandleElId(tipTitle.id);
11083                   dd.lock();
11084               }
11085               inited = true;
11086           }
11087           this.enable(); 
11088        },
11089
11090     /**
11091      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11092      * are supported:
11093      * <pre>
11094 Property    Type                   Description
11095 ----------  ---------------------  ------------------------------------------------------------------------
11096 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11097      * </ul>
11098      * @param {Object} config The config object
11099      */
11100        register : function(config){
11101            var cs = config instanceof Array ? config : arguments;
11102            for(var i = 0, len = cs.length; i < len; i++) {
11103                var c = cs[i];
11104                var target = c.target;
11105                if(target){
11106                    if(target instanceof Array){
11107                        for(var j = 0, jlen = target.length; j < jlen; j++){
11108                            tagEls[target[j]] = c;
11109                        }
11110                    }else{
11111                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11112                    }
11113                }
11114            }
11115        },
11116
11117     /**
11118      * Removes this quick tip from its element and destroys it.
11119      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11120      */
11121        unregister : function(el){
11122            delete tagEls[Roo.id(el)];
11123        },
11124
11125     /**
11126      * Enable this quick tip.
11127      */
11128        enable : function(){
11129            if(inited && disabled){
11130                locks.pop();
11131                if(locks.length < 1){
11132                    disabled = false;
11133                }
11134            }
11135        },
11136
11137     /**
11138      * Disable this quick tip.
11139      */
11140        disable : function(){
11141           disabled = true;
11142           clearTimeout(showProc);
11143           clearTimeout(hideProc);
11144           clearTimeout(dismissProc);
11145           if(ce){
11146               hide(true);
11147           }
11148           locks.push(1);
11149        },
11150
11151     /**
11152      * Returns true if the quick tip is enabled, else false.
11153      */
11154        isEnabled : function(){
11155             return !disabled;
11156        },
11157
11158         // private
11159        tagConfig : {
11160            namespace : "roo", // was ext?? this may break..
11161            alt_namespace : "ext",
11162            attribute : "qtip",
11163            width : "width",
11164            target : "target",
11165            title : "qtitle",
11166            hide : "hide",
11167            cls : "qclass"
11168        }
11169    };
11170 }();
11171
11172 // backwards compat
11173 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11174  * Based on:
11175  * Ext JS Library 1.1.1
11176  * Copyright(c) 2006-2007, Ext JS, LLC.
11177  *
11178  * Originally Released Under LGPL - original licence link has changed is not relivant.
11179  *
11180  * Fork - LGPL
11181  * <script type="text/javascript">
11182  */
11183  
11184
11185 /**
11186  * @class Roo.tree.TreePanel
11187  * @extends Roo.data.Tree
11188
11189  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11190  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11191  * @cfg {Boolean} enableDD true to enable drag and drop
11192  * @cfg {Boolean} enableDrag true to enable just drag
11193  * @cfg {Boolean} enableDrop true to enable just drop
11194  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11195  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11196  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11197  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11198  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11199  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11200  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11201  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11202  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11203  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11204  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11205  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11206  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11207  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11208  * @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>
11209  * @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>
11210  * 
11211  * @constructor
11212  * @param {String/HTMLElement/Element} el The container element
11213  * @param {Object} config
11214  */
11215 Roo.tree.TreePanel = function(el, config){
11216     var root = false;
11217     var loader = false;
11218     if (config.root) {
11219         root = config.root;
11220         delete config.root;
11221     }
11222     if (config.loader) {
11223         loader = config.loader;
11224         delete config.loader;
11225     }
11226     
11227     Roo.apply(this, config);
11228     Roo.tree.TreePanel.superclass.constructor.call(this);
11229     this.el = Roo.get(el);
11230     this.el.addClass('x-tree');
11231     //console.log(root);
11232     if (root) {
11233         this.setRootNode( Roo.factory(root, Roo.tree));
11234     }
11235     if (loader) {
11236         this.loader = Roo.factory(loader, Roo.tree);
11237     }
11238    /**
11239     * Read-only. The id of the container element becomes this TreePanel's id.
11240     */
11241     this.id = this.el.id;
11242     this.addEvents({
11243         /**
11244         * @event beforeload
11245         * Fires before a node is loaded, return false to cancel
11246         * @param {Node} node The node being loaded
11247         */
11248         "beforeload" : true,
11249         /**
11250         * @event load
11251         * Fires when a node is loaded
11252         * @param {Node} node The node that was loaded
11253         */
11254         "load" : true,
11255         /**
11256         * @event textchange
11257         * Fires when the text for a node is changed
11258         * @param {Node} node The node
11259         * @param {String} text The new text
11260         * @param {String} oldText The old text
11261         */
11262         "textchange" : true,
11263         /**
11264         * @event beforeexpand
11265         * Fires before a node is expanded, return false to cancel.
11266         * @param {Node} node The node
11267         * @param {Boolean} deep
11268         * @param {Boolean} anim
11269         */
11270         "beforeexpand" : true,
11271         /**
11272         * @event beforecollapse
11273         * Fires before a node is collapsed, return false to cancel.
11274         * @param {Node} node The node
11275         * @param {Boolean} deep
11276         * @param {Boolean} anim
11277         */
11278         "beforecollapse" : true,
11279         /**
11280         * @event expand
11281         * Fires when a node is expanded
11282         * @param {Node} node The node
11283         */
11284         "expand" : true,
11285         /**
11286         * @event disabledchange
11287         * Fires when the disabled status of a node changes
11288         * @param {Node} node The node
11289         * @param {Boolean} disabled
11290         */
11291         "disabledchange" : true,
11292         /**
11293         * @event collapse
11294         * Fires when a node is collapsed
11295         * @param {Node} node The node
11296         */
11297         "collapse" : true,
11298         /**
11299         * @event beforeclick
11300         * Fires before click processing on a node. Return false to cancel the default action.
11301         * @param {Node} node The node
11302         * @param {Roo.EventObject} e The event object
11303         */
11304         "beforeclick":true,
11305         /**
11306         * @event checkchange
11307         * Fires when a node with a checkbox's checked property changes
11308         * @param {Node} this This node
11309         * @param {Boolean} checked
11310         */
11311         "checkchange":true,
11312         /**
11313         * @event click
11314         * Fires when a node is clicked
11315         * @param {Node} node The node
11316         * @param {Roo.EventObject} e The event object
11317         */
11318         "click":true,
11319         /**
11320         * @event dblclick
11321         * Fires when a node is double clicked
11322         * @param {Node} node The node
11323         * @param {Roo.EventObject} e The event object
11324         */
11325         "dblclick":true,
11326         /**
11327         * @event contextmenu
11328         * Fires when a node is right clicked
11329         * @param {Node} node The node
11330         * @param {Roo.EventObject} e The event object
11331         */
11332         "contextmenu":true,
11333         /**
11334         * @event beforechildrenrendered
11335         * Fires right before the child nodes for a node are rendered
11336         * @param {Node} node The node
11337         */
11338         "beforechildrenrendered":true,
11339         /**
11340         * @event startdrag
11341         * Fires when a node starts being dragged
11342         * @param {Roo.tree.TreePanel} this
11343         * @param {Roo.tree.TreeNode} node
11344         * @param {event} e The raw browser event
11345         */ 
11346        "startdrag" : true,
11347        /**
11348         * @event enddrag
11349         * Fires when a drag operation is complete
11350         * @param {Roo.tree.TreePanel} this
11351         * @param {Roo.tree.TreeNode} node
11352         * @param {event} e The raw browser event
11353         */
11354        "enddrag" : true,
11355        /**
11356         * @event dragdrop
11357         * Fires when a dragged node is dropped on a valid DD target
11358         * @param {Roo.tree.TreePanel} this
11359         * @param {Roo.tree.TreeNode} node
11360         * @param {DD} dd The dd it was dropped on
11361         * @param {event} e The raw browser event
11362         */
11363        "dragdrop" : true,
11364        /**
11365         * @event beforenodedrop
11366         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11367         * passed to handlers has the following properties:<br />
11368         * <ul style="padding:5px;padding-left:16px;">
11369         * <li>tree - The TreePanel</li>
11370         * <li>target - The node being targeted for the drop</li>
11371         * <li>data - The drag data from the drag source</li>
11372         * <li>point - The point of the drop - append, above or below</li>
11373         * <li>source - The drag source</li>
11374         * <li>rawEvent - Raw mouse event</li>
11375         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11376         * to be inserted by setting them on this object.</li>
11377         * <li>cancel - Set this to true to cancel the drop.</li>
11378         * </ul>
11379         * @param {Object} dropEvent
11380         */
11381        "beforenodedrop" : true,
11382        /**
11383         * @event nodedrop
11384         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11385         * passed to handlers has the following properties:<br />
11386         * <ul style="padding:5px;padding-left:16px;">
11387         * <li>tree - The TreePanel</li>
11388         * <li>target - The node being targeted for the drop</li>
11389         * <li>data - The drag data from the drag source</li>
11390         * <li>point - The point of the drop - append, above or below</li>
11391         * <li>source - The drag source</li>
11392         * <li>rawEvent - Raw mouse event</li>
11393         * <li>dropNode - Dropped node(s).</li>
11394         * </ul>
11395         * @param {Object} dropEvent
11396         */
11397        "nodedrop" : true,
11398         /**
11399         * @event nodedragover
11400         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11401         * passed to handlers has the following properties:<br />
11402         * <ul style="padding:5px;padding-left:16px;">
11403         * <li>tree - The TreePanel</li>
11404         * <li>target - The node being targeted for the drop</li>
11405         * <li>data - The drag data from the drag source</li>
11406         * <li>point - The point of the drop - append, above or below</li>
11407         * <li>source - The drag source</li>
11408         * <li>rawEvent - Raw mouse event</li>
11409         * <li>dropNode - Drop node(s) provided by the source.</li>
11410         * <li>cancel - Set this to true to signal drop not allowed.</li>
11411         * </ul>
11412         * @param {Object} dragOverEvent
11413         */
11414        "nodedragover" : true,
11415        /**
11416         * @event appendnode
11417         * Fires when append node to the tree
11418         * @param {Roo.tree.TreePanel} this
11419         * @param {Roo.tree.TreeNode} node
11420         * @param {Number} index The index of the newly appended node
11421         */
11422        "appendnode" : true
11423         
11424     });
11425     if(this.singleExpand){
11426        this.on("beforeexpand", this.restrictExpand, this);
11427     }
11428     if (this.editor) {
11429         this.editor.tree = this;
11430         this.editor = Roo.factory(this.editor, Roo.tree);
11431     }
11432     
11433     if (this.selModel) {
11434         this.selModel = Roo.factory(this.selModel, Roo.tree);
11435     }
11436    
11437 };
11438 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11439     rootVisible : true,
11440     animate: Roo.enableFx,
11441     lines : true,
11442     enableDD : false,
11443     hlDrop : Roo.enableFx,
11444   
11445     renderer: false,
11446     
11447     rendererTip: false,
11448     // private
11449     restrictExpand : function(node){
11450         var p = node.parentNode;
11451         if(p){
11452             if(p.expandedChild && p.expandedChild.parentNode == p){
11453                 p.expandedChild.collapse();
11454             }
11455             p.expandedChild = node;
11456         }
11457     },
11458
11459     // private override
11460     setRootNode : function(node){
11461         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11462         if(!this.rootVisible){
11463             node.ui = new Roo.tree.RootTreeNodeUI(node);
11464         }
11465         return node;
11466     },
11467
11468     /**
11469      * Returns the container element for this TreePanel
11470      */
11471     getEl : function(){
11472         return this.el;
11473     },
11474
11475     /**
11476      * Returns the default TreeLoader for this TreePanel
11477      */
11478     getLoader : function(){
11479         return this.loader;
11480     },
11481
11482     /**
11483      * Expand all nodes
11484      */
11485     expandAll : function(){
11486         this.root.expand(true);
11487     },
11488
11489     /**
11490      * Collapse all nodes
11491      */
11492     collapseAll : function(){
11493         this.root.collapse(true);
11494     },
11495
11496     /**
11497      * Returns the selection model used by this TreePanel
11498      */
11499     getSelectionModel : function(){
11500         if(!this.selModel){
11501             this.selModel = new Roo.tree.DefaultSelectionModel();
11502         }
11503         return this.selModel;
11504     },
11505
11506     /**
11507      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11508      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11509      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11510      * @return {Array}
11511      */
11512     getChecked : function(a, startNode){
11513         startNode = startNode || this.root;
11514         var r = [];
11515         var f = function(){
11516             if(this.attributes.checked){
11517                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11518             }
11519         }
11520         startNode.cascade(f);
11521         return r;
11522     },
11523
11524     /**
11525      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11526      * @param {String} path
11527      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11528      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11529      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11530      */
11531     expandPath : function(path, attr, callback){
11532         attr = attr || "id";
11533         var keys = path.split(this.pathSeparator);
11534         var curNode = this.root;
11535         if(curNode.attributes[attr] != keys[1]){ // invalid root
11536             if(callback){
11537                 callback(false, null);
11538             }
11539             return;
11540         }
11541         var index = 1;
11542         var f = function(){
11543             if(++index == keys.length){
11544                 if(callback){
11545                     callback(true, curNode);
11546                 }
11547                 return;
11548             }
11549             var c = curNode.findChild(attr, keys[index]);
11550             if(!c){
11551                 if(callback){
11552                     callback(false, curNode);
11553                 }
11554                 return;
11555             }
11556             curNode = c;
11557             c.expand(false, false, f);
11558         };
11559         curNode.expand(false, false, f);
11560     },
11561
11562     /**
11563      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11564      * @param {String} path
11565      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11566      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11567      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11568      */
11569     selectPath : function(path, attr, callback){
11570         attr = attr || "id";
11571         var keys = path.split(this.pathSeparator);
11572         var v = keys.pop();
11573         if(keys.length > 0){
11574             var f = function(success, node){
11575                 if(success && node){
11576                     var n = node.findChild(attr, v);
11577                     if(n){
11578                         n.select();
11579                         if(callback){
11580                             callback(true, n);
11581                         }
11582                     }else if(callback){
11583                         callback(false, n);
11584                     }
11585                 }else{
11586                     if(callback){
11587                         callback(false, n);
11588                     }
11589                 }
11590             };
11591             this.expandPath(keys.join(this.pathSeparator), attr, f);
11592         }else{
11593             this.root.select();
11594             if(callback){
11595                 callback(true, this.root);
11596             }
11597         }
11598     },
11599
11600     getTreeEl : function(){
11601         return this.el;
11602     },
11603
11604     /**
11605      * Trigger rendering of this TreePanel
11606      */
11607     render : function(){
11608         if (this.innerCt) {
11609             return this; // stop it rendering more than once!!
11610         }
11611         
11612         this.innerCt = this.el.createChild({tag:"ul",
11613                cls:"x-tree-root-ct " +
11614                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11615
11616         if(this.containerScroll){
11617             Roo.dd.ScrollManager.register(this.el);
11618         }
11619         if((this.enableDD || this.enableDrop) && !this.dropZone){
11620            /**
11621             * The dropZone used by this tree if drop is enabled
11622             * @type Roo.tree.TreeDropZone
11623             */
11624              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11625                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11626            });
11627         }
11628         if((this.enableDD || this.enableDrag) && !this.dragZone){
11629            /**
11630             * The dragZone used by this tree if drag is enabled
11631             * @type Roo.tree.TreeDragZone
11632             */
11633             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11634                ddGroup: this.ddGroup || "TreeDD",
11635                scroll: this.ddScroll
11636            });
11637         }
11638         this.getSelectionModel().init(this);
11639         if (!this.root) {
11640             Roo.log("ROOT not set in tree");
11641             return this;
11642         }
11643         this.root.render();
11644         if(!this.rootVisible){
11645             this.root.renderChildren();
11646         }
11647         return this;
11648     }
11649 });/*
11650  * Based on:
11651  * Ext JS Library 1.1.1
11652  * Copyright(c) 2006-2007, Ext JS, LLC.
11653  *
11654  * Originally Released Under LGPL - original licence link has changed is not relivant.
11655  *
11656  * Fork - LGPL
11657  * <script type="text/javascript">
11658  */
11659  
11660
11661 /**
11662  * @class Roo.tree.DefaultSelectionModel
11663  * @extends Roo.util.Observable
11664  * The default single selection for a TreePanel.
11665  * @param {Object} cfg Configuration
11666  */
11667 Roo.tree.DefaultSelectionModel = function(cfg){
11668    this.selNode = null;
11669    
11670    
11671    
11672    this.addEvents({
11673        /**
11674         * @event selectionchange
11675         * Fires when the selected node changes
11676         * @param {DefaultSelectionModel} this
11677         * @param {TreeNode} node the new selection
11678         */
11679        "selectionchange" : true,
11680
11681        /**
11682         * @event beforeselect
11683         * Fires before the selected node changes, return false to cancel the change
11684         * @param {DefaultSelectionModel} this
11685         * @param {TreeNode} node the new selection
11686         * @param {TreeNode} node the old selection
11687         */
11688        "beforeselect" : true
11689    });
11690    
11691     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11692 };
11693
11694 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11695     init : function(tree){
11696         this.tree = tree;
11697         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11698         tree.on("click", this.onNodeClick, this);
11699     },
11700     
11701     onNodeClick : function(node, e){
11702         if (e.ctrlKey && this.selNode == node)  {
11703             this.unselect(node);
11704             return;
11705         }
11706         this.select(node);
11707     },
11708     
11709     /**
11710      * Select a node.
11711      * @param {TreeNode} node The node to select
11712      * @return {TreeNode} The selected node
11713      */
11714     select : function(node){
11715         var last = this.selNode;
11716         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11717             if(last){
11718                 last.ui.onSelectedChange(false);
11719             }
11720             this.selNode = node;
11721             node.ui.onSelectedChange(true);
11722             this.fireEvent("selectionchange", this, node, last);
11723         }
11724         return node;
11725     },
11726     
11727     /**
11728      * Deselect a node.
11729      * @param {TreeNode} node The node to unselect
11730      */
11731     unselect : function(node){
11732         if(this.selNode == node){
11733             this.clearSelections();
11734         }    
11735     },
11736     
11737     /**
11738      * Clear all selections
11739      */
11740     clearSelections : function(){
11741         var n = this.selNode;
11742         if(n){
11743             n.ui.onSelectedChange(false);
11744             this.selNode = null;
11745             this.fireEvent("selectionchange", this, null);
11746         }
11747         return n;
11748     },
11749     
11750     /**
11751      * Get the selected node
11752      * @return {TreeNode} The selected node
11753      */
11754     getSelectedNode : function(){
11755         return this.selNode;    
11756     },
11757     
11758     /**
11759      * Returns true if the node is selected
11760      * @param {TreeNode} node The node to check
11761      * @return {Boolean}
11762      */
11763     isSelected : function(node){
11764         return this.selNode == node;  
11765     },
11766
11767     /**
11768      * Selects the node above the selected node in the tree, intelligently walking the nodes
11769      * @return TreeNode The new selection
11770      */
11771     selectPrevious : function(){
11772         var s = this.selNode || this.lastSelNode;
11773         if(!s){
11774             return null;
11775         }
11776         var ps = s.previousSibling;
11777         if(ps){
11778             if(!ps.isExpanded() || ps.childNodes.length < 1){
11779                 return this.select(ps);
11780             } else{
11781                 var lc = ps.lastChild;
11782                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11783                     lc = lc.lastChild;
11784                 }
11785                 return this.select(lc);
11786             }
11787         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11788             return this.select(s.parentNode);
11789         }
11790         return null;
11791     },
11792
11793     /**
11794      * Selects the node above the selected node in the tree, intelligently walking the nodes
11795      * @return TreeNode The new selection
11796      */
11797     selectNext : function(){
11798         var s = this.selNode || this.lastSelNode;
11799         if(!s){
11800             return null;
11801         }
11802         if(s.firstChild && s.isExpanded()){
11803              return this.select(s.firstChild);
11804          }else if(s.nextSibling){
11805              return this.select(s.nextSibling);
11806          }else if(s.parentNode){
11807             var newS = null;
11808             s.parentNode.bubble(function(){
11809                 if(this.nextSibling){
11810                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11811                     return false;
11812                 }
11813             });
11814             return newS;
11815          }
11816         return null;
11817     },
11818
11819     onKeyDown : function(e){
11820         var s = this.selNode || this.lastSelNode;
11821         // undesirable, but required
11822         var sm = this;
11823         if(!s){
11824             return;
11825         }
11826         var k = e.getKey();
11827         switch(k){
11828              case e.DOWN:
11829                  e.stopEvent();
11830                  this.selectNext();
11831              break;
11832              case e.UP:
11833                  e.stopEvent();
11834                  this.selectPrevious();
11835              break;
11836              case e.RIGHT:
11837                  e.preventDefault();
11838                  if(s.hasChildNodes()){
11839                      if(!s.isExpanded()){
11840                          s.expand();
11841                      }else if(s.firstChild){
11842                          this.select(s.firstChild, e);
11843                      }
11844                  }
11845              break;
11846              case e.LEFT:
11847                  e.preventDefault();
11848                  if(s.hasChildNodes() && s.isExpanded()){
11849                      s.collapse();
11850                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11851                      this.select(s.parentNode, e);
11852                  }
11853              break;
11854         };
11855     }
11856 });
11857
11858 /**
11859  * @class Roo.tree.MultiSelectionModel
11860  * @extends Roo.util.Observable
11861  * Multi selection for a TreePanel.
11862  * @param {Object} cfg Configuration
11863  */
11864 Roo.tree.MultiSelectionModel = function(){
11865    this.selNodes = [];
11866    this.selMap = {};
11867    this.addEvents({
11868        /**
11869         * @event selectionchange
11870         * Fires when the selected nodes change
11871         * @param {MultiSelectionModel} this
11872         * @param {Array} nodes Array of the selected nodes
11873         */
11874        "selectionchange" : true
11875    });
11876    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11877    
11878 };
11879
11880 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11881     init : function(tree){
11882         this.tree = tree;
11883         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11884         tree.on("click", this.onNodeClick, this);
11885     },
11886     
11887     onNodeClick : function(node, e){
11888         this.select(node, e, e.ctrlKey);
11889     },
11890     
11891     /**
11892      * Select a node.
11893      * @param {TreeNode} node The node to select
11894      * @param {EventObject} e (optional) An event associated with the selection
11895      * @param {Boolean} keepExisting True to retain existing selections
11896      * @return {TreeNode} The selected node
11897      */
11898     select : function(node, e, keepExisting){
11899         if(keepExisting !== true){
11900             this.clearSelections(true);
11901         }
11902         if(this.isSelected(node)){
11903             this.lastSelNode = node;
11904             return node;
11905         }
11906         this.selNodes.push(node);
11907         this.selMap[node.id] = node;
11908         this.lastSelNode = node;
11909         node.ui.onSelectedChange(true);
11910         this.fireEvent("selectionchange", this, this.selNodes);
11911         return node;
11912     },
11913     
11914     /**
11915      * Deselect a node.
11916      * @param {TreeNode} node The node to unselect
11917      */
11918     unselect : function(node){
11919         if(this.selMap[node.id]){
11920             node.ui.onSelectedChange(false);
11921             var sn = this.selNodes;
11922             var index = -1;
11923             if(sn.indexOf){
11924                 index = sn.indexOf(node);
11925             }else{
11926                 for(var i = 0, len = sn.length; i < len; i++){
11927                     if(sn[i] == node){
11928                         index = i;
11929                         break;
11930                     }
11931                 }
11932             }
11933             if(index != -1){
11934                 this.selNodes.splice(index, 1);
11935             }
11936             delete this.selMap[node.id];
11937             this.fireEvent("selectionchange", this, this.selNodes);
11938         }
11939     },
11940     
11941     /**
11942      * Clear all selections
11943      */
11944     clearSelections : function(suppressEvent){
11945         var sn = this.selNodes;
11946         if(sn.length > 0){
11947             for(var i = 0, len = sn.length; i < len; i++){
11948                 sn[i].ui.onSelectedChange(false);
11949             }
11950             this.selNodes = [];
11951             this.selMap = {};
11952             if(suppressEvent !== true){
11953                 this.fireEvent("selectionchange", this, this.selNodes);
11954             }
11955         }
11956     },
11957     
11958     /**
11959      * Returns true if the node is selected
11960      * @param {TreeNode} node The node to check
11961      * @return {Boolean}
11962      */
11963     isSelected : function(node){
11964         return this.selMap[node.id] ? true : false;  
11965     },
11966     
11967     /**
11968      * Returns an array of the selected nodes
11969      * @return {Array}
11970      */
11971     getSelectedNodes : function(){
11972         return this.selNodes;    
11973     },
11974
11975     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
11976
11977     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
11978
11979     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
11980 });/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990  
11991 /**
11992  * @class Roo.tree.TreeNode
11993  * @extends Roo.data.Node
11994  * @cfg {String} text The text for this node
11995  * @cfg {Boolean} expanded true to start the node expanded
11996  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
11997  * @cfg {Boolean} allowDrop false if this node cannot be drop on
11998  * @cfg {Boolean} disabled true to start the node disabled
11999  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12000  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12001  * @cfg {String} cls A css class to be added to the node
12002  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12003  * @cfg {String} href URL of the link used for the node (defaults to #)
12004  * @cfg {String} hrefTarget target frame for the link
12005  * @cfg {String} qtip An Ext QuickTip for the node
12006  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12007  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12008  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12009  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12010  * (defaults to undefined with no checkbox rendered)
12011  * @constructor
12012  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12013  */
12014 Roo.tree.TreeNode = function(attributes){
12015     attributes = attributes || {};
12016     if(typeof attributes == "string"){
12017         attributes = {text: attributes};
12018     }
12019     this.childrenRendered = false;
12020     this.rendered = false;
12021     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12022     this.expanded = attributes.expanded === true;
12023     this.isTarget = attributes.isTarget !== false;
12024     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12025     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12026
12027     /**
12028      * Read-only. The text for this node. To change it use setText().
12029      * @type String
12030      */
12031     this.text = attributes.text;
12032     /**
12033      * True if this node is disabled.
12034      * @type Boolean
12035      */
12036     this.disabled = attributes.disabled === true;
12037
12038     this.addEvents({
12039         /**
12040         * @event textchange
12041         * Fires when the text for this node is changed
12042         * @param {Node} this This node
12043         * @param {String} text The new text
12044         * @param {String} oldText The old text
12045         */
12046         "textchange" : true,
12047         /**
12048         * @event beforeexpand
12049         * Fires before this node is expanded, return false to cancel.
12050         * @param {Node} this This node
12051         * @param {Boolean} deep
12052         * @param {Boolean} anim
12053         */
12054         "beforeexpand" : true,
12055         /**
12056         * @event beforecollapse
12057         * Fires before this node is collapsed, return false to cancel.
12058         * @param {Node} this This node
12059         * @param {Boolean} deep
12060         * @param {Boolean} anim
12061         */
12062         "beforecollapse" : true,
12063         /**
12064         * @event expand
12065         * Fires when this node is expanded
12066         * @param {Node} this This node
12067         */
12068         "expand" : true,
12069         /**
12070         * @event disabledchange
12071         * Fires when the disabled status of this node changes
12072         * @param {Node} this This node
12073         * @param {Boolean} disabled
12074         */
12075         "disabledchange" : true,
12076         /**
12077         * @event collapse
12078         * Fires when this node is collapsed
12079         * @param {Node} this This node
12080         */
12081         "collapse" : true,
12082         /**
12083         * @event beforeclick
12084         * Fires before click processing. Return false to cancel the default action.
12085         * @param {Node} this This node
12086         * @param {Roo.EventObject} e The event object
12087         */
12088         "beforeclick":true,
12089         /**
12090         * @event checkchange
12091         * Fires when a node with a checkbox's checked property changes
12092         * @param {Node} this This node
12093         * @param {Boolean} checked
12094         */
12095         "checkchange":true,
12096         /**
12097         * @event click
12098         * Fires when this node is clicked
12099         * @param {Node} this This node
12100         * @param {Roo.EventObject} e The event object
12101         */
12102         "click":true,
12103         /**
12104         * @event dblclick
12105         * Fires when this node is double clicked
12106         * @param {Node} this This node
12107         * @param {Roo.EventObject} e The event object
12108         */
12109         "dblclick":true,
12110         /**
12111         * @event contextmenu
12112         * Fires when this node is right clicked
12113         * @param {Node} this This node
12114         * @param {Roo.EventObject} e The event object
12115         */
12116         "contextmenu":true,
12117         /**
12118         * @event beforechildrenrendered
12119         * Fires right before the child nodes for this node are rendered
12120         * @param {Node} this This node
12121         */
12122         "beforechildrenrendered":true
12123     });
12124
12125     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12126
12127     /**
12128      * Read-only. The UI for this node
12129      * @type TreeNodeUI
12130      */
12131     this.ui = new uiClass(this);
12132     
12133     // finally support items[]
12134     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12135         return;
12136     }
12137     
12138     
12139     Roo.each(this.attributes.items, function(c) {
12140         this.appendChild(Roo.factory(c,Roo.Tree));
12141     }, this);
12142     delete this.attributes.items;
12143     
12144     
12145     
12146 };
12147 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12148     preventHScroll: true,
12149     /**
12150      * Returns true if this node is expanded
12151      * @return {Boolean}
12152      */
12153     isExpanded : function(){
12154         return this.expanded;
12155     },
12156
12157     /**
12158      * Returns the UI object for this node
12159      * @return {TreeNodeUI}
12160      */
12161     getUI : function(){
12162         return this.ui;
12163     },
12164
12165     // private override
12166     setFirstChild : function(node){
12167         var of = this.firstChild;
12168         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12169         if(this.childrenRendered && of && node != of){
12170             of.renderIndent(true, true);
12171         }
12172         if(this.rendered){
12173             this.renderIndent(true, true);
12174         }
12175     },
12176
12177     // private override
12178     setLastChild : function(node){
12179         var ol = this.lastChild;
12180         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12181         if(this.childrenRendered && ol && node != ol){
12182             ol.renderIndent(true, true);
12183         }
12184         if(this.rendered){
12185             this.renderIndent(true, true);
12186         }
12187     },
12188
12189     // these methods are overridden to provide lazy rendering support
12190     // private override
12191     appendChild : function()
12192     {
12193         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12194         if(node && this.childrenRendered){
12195             node.render();
12196         }
12197         this.ui.updateExpandIcon();
12198         return node;
12199     },
12200
12201     // private override
12202     removeChild : function(node){
12203         this.ownerTree.getSelectionModel().unselect(node);
12204         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12205         // if it's been rendered remove dom node
12206         if(this.childrenRendered){
12207             node.ui.remove();
12208         }
12209         if(this.childNodes.length < 1){
12210             this.collapse(false, false);
12211         }else{
12212             this.ui.updateExpandIcon();
12213         }
12214         if(!this.firstChild) {
12215             this.childrenRendered = false;
12216         }
12217         return node;
12218     },
12219
12220     // private override
12221     insertBefore : function(node, refNode){
12222         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12223         if(newNode && refNode && this.childrenRendered){
12224             node.render();
12225         }
12226         this.ui.updateExpandIcon();
12227         return newNode;
12228     },
12229
12230     /**
12231      * Sets the text for this node
12232      * @param {String} text
12233      */
12234     setText : function(text){
12235         var oldText = this.text;
12236         this.text = text;
12237         this.attributes.text = text;
12238         if(this.rendered){ // event without subscribing
12239             this.ui.onTextChange(this, text, oldText);
12240         }
12241         this.fireEvent("textchange", this, text, oldText);
12242     },
12243
12244     /**
12245      * Triggers selection of this node
12246      */
12247     select : function(){
12248         this.getOwnerTree().getSelectionModel().select(this);
12249     },
12250
12251     /**
12252      * Triggers deselection of this node
12253      */
12254     unselect : function(){
12255         this.getOwnerTree().getSelectionModel().unselect(this);
12256     },
12257
12258     /**
12259      * Returns true if this node is selected
12260      * @return {Boolean}
12261      */
12262     isSelected : function(){
12263         return this.getOwnerTree().getSelectionModel().isSelected(this);
12264     },
12265
12266     /**
12267      * Expand this node.
12268      * @param {Boolean} deep (optional) True to expand all children as well
12269      * @param {Boolean} anim (optional) false to cancel the default animation
12270      * @param {Function} callback (optional) A callback to be called when
12271      * expanding this node completes (does not wait for deep expand to complete).
12272      * Called with 1 parameter, this node.
12273      */
12274     expand : function(deep, anim, callback){
12275         if(!this.expanded){
12276             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12277                 return;
12278             }
12279             if(!this.childrenRendered){
12280                 this.renderChildren();
12281             }
12282             this.expanded = true;
12283             
12284             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12285                 this.ui.animExpand(function(){
12286                     this.fireEvent("expand", this);
12287                     if(typeof callback == "function"){
12288                         callback(this);
12289                     }
12290                     if(deep === true){
12291                         this.expandChildNodes(true);
12292                     }
12293                 }.createDelegate(this));
12294                 return;
12295             }else{
12296                 this.ui.expand();
12297                 this.fireEvent("expand", this);
12298                 if(typeof callback == "function"){
12299                     callback(this);
12300                 }
12301             }
12302         }else{
12303            if(typeof callback == "function"){
12304                callback(this);
12305            }
12306         }
12307         if(deep === true){
12308             this.expandChildNodes(true);
12309         }
12310     },
12311
12312     isHiddenRoot : function(){
12313         return this.isRoot && !this.getOwnerTree().rootVisible;
12314     },
12315
12316     /**
12317      * Collapse this node.
12318      * @param {Boolean} deep (optional) True to collapse all children as well
12319      * @param {Boolean} anim (optional) false to cancel the default animation
12320      */
12321     collapse : function(deep, anim){
12322         if(this.expanded && !this.isHiddenRoot()){
12323             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12324                 return;
12325             }
12326             this.expanded = false;
12327             if((this.getOwnerTree().animate && anim !== false) || anim){
12328                 this.ui.animCollapse(function(){
12329                     this.fireEvent("collapse", this);
12330                     if(deep === true){
12331                         this.collapseChildNodes(true);
12332                     }
12333                 }.createDelegate(this));
12334                 return;
12335             }else{
12336                 this.ui.collapse();
12337                 this.fireEvent("collapse", this);
12338             }
12339         }
12340         if(deep === true){
12341             var cs = this.childNodes;
12342             for(var i = 0, len = cs.length; i < len; i++) {
12343                 cs[i].collapse(true, false);
12344             }
12345         }
12346     },
12347
12348     // private
12349     delayedExpand : function(delay){
12350         if(!this.expandProcId){
12351             this.expandProcId = this.expand.defer(delay, this);
12352         }
12353     },
12354
12355     // private
12356     cancelExpand : function(){
12357         if(this.expandProcId){
12358             clearTimeout(this.expandProcId);
12359         }
12360         this.expandProcId = false;
12361     },
12362
12363     /**
12364      * Toggles expanded/collapsed state of the node
12365      */
12366     toggle : function(){
12367         if(this.expanded){
12368             this.collapse();
12369         }else{
12370             this.expand();
12371         }
12372     },
12373
12374     /**
12375      * Ensures all parent nodes are expanded
12376      */
12377     ensureVisible : function(callback){
12378         var tree = this.getOwnerTree();
12379         tree.expandPath(this.parentNode.getPath(), false, function(){
12380             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12381             Roo.callback(callback);
12382         }.createDelegate(this));
12383     },
12384
12385     /**
12386      * Expand all child nodes
12387      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12388      */
12389     expandChildNodes : function(deep){
12390         var cs = this.childNodes;
12391         for(var i = 0, len = cs.length; i < len; i++) {
12392                 cs[i].expand(deep);
12393         }
12394     },
12395
12396     /**
12397      * Collapse all child nodes
12398      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12399      */
12400     collapseChildNodes : function(deep){
12401         var cs = this.childNodes;
12402         for(var i = 0, len = cs.length; i < len; i++) {
12403                 cs[i].collapse(deep);
12404         }
12405     },
12406
12407     /**
12408      * Disables this node
12409      */
12410     disable : function(){
12411         this.disabled = true;
12412         this.unselect();
12413         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12414             this.ui.onDisableChange(this, true);
12415         }
12416         this.fireEvent("disabledchange", this, true);
12417     },
12418
12419     /**
12420      * Enables this node
12421      */
12422     enable : function(){
12423         this.disabled = false;
12424         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12425             this.ui.onDisableChange(this, false);
12426         }
12427         this.fireEvent("disabledchange", this, false);
12428     },
12429
12430     // private
12431     renderChildren : function(suppressEvent){
12432         if(suppressEvent !== false){
12433             this.fireEvent("beforechildrenrendered", this);
12434         }
12435         var cs = this.childNodes;
12436         for(var i = 0, len = cs.length; i < len; i++){
12437             cs[i].render(true);
12438         }
12439         this.childrenRendered = true;
12440     },
12441
12442     // private
12443     sort : function(fn, scope){
12444         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12445         if(this.childrenRendered){
12446             var cs = this.childNodes;
12447             for(var i = 0, len = cs.length; i < len; i++){
12448                 cs[i].render(true);
12449             }
12450         }
12451     },
12452
12453     // private
12454     render : function(bulkRender){
12455         this.ui.render(bulkRender);
12456         if(!this.rendered){
12457             this.rendered = true;
12458             if(this.expanded){
12459                 this.expanded = false;
12460                 this.expand(false, false);
12461             }
12462         }
12463     },
12464
12465     // private
12466     renderIndent : function(deep, refresh){
12467         if(refresh){
12468             this.ui.childIndent = null;
12469         }
12470         this.ui.renderIndent();
12471         if(deep === true && this.childrenRendered){
12472             var cs = this.childNodes;
12473             for(var i = 0, len = cs.length; i < len; i++){
12474                 cs[i].renderIndent(true, refresh);
12475             }
12476         }
12477     }
12478 });/*
12479  * Based on:
12480  * Ext JS Library 1.1.1
12481  * Copyright(c) 2006-2007, Ext JS, LLC.
12482  *
12483  * Originally Released Under LGPL - original licence link has changed is not relivant.
12484  *
12485  * Fork - LGPL
12486  * <script type="text/javascript">
12487  */
12488  
12489 /**
12490  * @class Roo.tree.AsyncTreeNode
12491  * @extends Roo.tree.TreeNode
12492  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12493  * @constructor
12494  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12495  */
12496  Roo.tree.AsyncTreeNode = function(config){
12497     this.loaded = false;
12498     this.loading = false;
12499     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12500     /**
12501     * @event beforeload
12502     * Fires before this node is loaded, return false to cancel
12503     * @param {Node} this This node
12504     */
12505     this.addEvents({'beforeload':true, 'load': true});
12506     /**
12507     * @event load
12508     * Fires when this node is loaded
12509     * @param {Node} this This node
12510     */
12511     /**
12512      * The loader used by this node (defaults to using the tree's defined loader)
12513      * @type TreeLoader
12514      * @property loader
12515      */
12516 };
12517 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12518     expand : function(deep, anim, callback){
12519         if(this.loading){ // if an async load is already running, waiting til it's done
12520             var timer;
12521             var f = function(){
12522                 if(!this.loading){ // done loading
12523                     clearInterval(timer);
12524                     this.expand(deep, anim, callback);
12525                 }
12526             }.createDelegate(this);
12527             timer = setInterval(f, 200);
12528             return;
12529         }
12530         if(!this.loaded){
12531             if(this.fireEvent("beforeload", this) === false){
12532                 return;
12533             }
12534             this.loading = true;
12535             this.ui.beforeLoad(this);
12536             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12537             if(loader){
12538                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12539                 return;
12540             }
12541         }
12542         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12543     },
12544     
12545     /**
12546      * Returns true if this node is currently loading
12547      * @return {Boolean}
12548      */
12549     isLoading : function(){
12550         return this.loading;  
12551     },
12552     
12553     loadComplete : function(deep, anim, callback){
12554         this.loading = false;
12555         this.loaded = true;
12556         this.ui.afterLoad(this);
12557         this.fireEvent("load", this);
12558         this.expand(deep, anim, callback);
12559     },
12560     
12561     /**
12562      * Returns true if this node has been loaded
12563      * @return {Boolean}
12564      */
12565     isLoaded : function(){
12566         return this.loaded;
12567     },
12568     
12569     hasChildNodes : function(){
12570         if(!this.isLeaf() && !this.loaded){
12571             return true;
12572         }else{
12573             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12574         }
12575     },
12576
12577     /**
12578      * Trigger a reload for this node
12579      * @param {Function} callback
12580      */
12581     reload : function(callback){
12582         this.collapse(false, false);
12583         while(this.firstChild){
12584             this.removeChild(this.firstChild);
12585         }
12586         this.childrenRendered = false;
12587         this.loaded = false;
12588         if(this.isHiddenRoot()){
12589             this.expanded = false;
12590         }
12591         this.expand(false, false, callback);
12592     }
12593 });/*
12594  * Based on:
12595  * Ext JS Library 1.1.1
12596  * Copyright(c) 2006-2007, Ext JS, LLC.
12597  *
12598  * Originally Released Under LGPL - original licence link has changed is not relivant.
12599  *
12600  * Fork - LGPL
12601  * <script type="text/javascript">
12602  */
12603  
12604 /**
12605  * @class Roo.tree.TreeNodeUI
12606  * @constructor
12607  * @param {Object} node The node to render
12608  * The TreeNode UI implementation is separate from the
12609  * tree implementation. Unless you are customizing the tree UI,
12610  * you should never have to use this directly.
12611  */
12612 Roo.tree.TreeNodeUI = function(node){
12613     this.node = node;
12614     this.rendered = false;
12615     this.animating = false;
12616     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12617 };
12618
12619 Roo.tree.TreeNodeUI.prototype = {
12620     removeChild : function(node){
12621         if(this.rendered){
12622             this.ctNode.removeChild(node.ui.getEl());
12623         }
12624     },
12625
12626     beforeLoad : function(){
12627          this.addClass("x-tree-node-loading");
12628     },
12629
12630     afterLoad : function(){
12631          this.removeClass("x-tree-node-loading");
12632     },
12633
12634     onTextChange : function(node, text, oldText){
12635         if(this.rendered){
12636             this.textNode.innerHTML = text;
12637         }
12638     },
12639
12640     onDisableChange : function(node, state){
12641         this.disabled = state;
12642         if(state){
12643             this.addClass("x-tree-node-disabled");
12644         }else{
12645             this.removeClass("x-tree-node-disabled");
12646         }
12647     },
12648
12649     onSelectedChange : function(state){
12650         if(state){
12651             this.focus();
12652             this.addClass("x-tree-selected");
12653         }else{
12654             //this.blur();
12655             this.removeClass("x-tree-selected");
12656         }
12657     },
12658
12659     onMove : function(tree, node, oldParent, newParent, index, refNode){
12660         this.childIndent = null;
12661         if(this.rendered){
12662             var targetNode = newParent.ui.getContainer();
12663             if(!targetNode){//target not rendered
12664                 this.holder = document.createElement("div");
12665                 this.holder.appendChild(this.wrap);
12666                 return;
12667             }
12668             var insertBefore = refNode ? refNode.ui.getEl() : null;
12669             if(insertBefore){
12670                 targetNode.insertBefore(this.wrap, insertBefore);
12671             }else{
12672                 targetNode.appendChild(this.wrap);
12673             }
12674             this.node.renderIndent(true);
12675         }
12676     },
12677
12678     addClass : function(cls){
12679         if(this.elNode){
12680             Roo.fly(this.elNode).addClass(cls);
12681         }
12682     },
12683
12684     removeClass : function(cls){
12685         if(this.elNode){
12686             Roo.fly(this.elNode).removeClass(cls);
12687         }
12688     },
12689
12690     remove : function(){
12691         if(this.rendered){
12692             this.holder = document.createElement("div");
12693             this.holder.appendChild(this.wrap);
12694         }
12695     },
12696
12697     fireEvent : function(){
12698         return this.node.fireEvent.apply(this.node, arguments);
12699     },
12700
12701     initEvents : function(){
12702         this.node.on("move", this.onMove, this);
12703         var E = Roo.EventManager;
12704         var a = this.anchor;
12705
12706         var el = Roo.fly(a, '_treeui');
12707
12708         if(Roo.isOpera){ // opera render bug ignores the CSS
12709             el.setStyle("text-decoration", "none");
12710         }
12711
12712         el.on("click", this.onClick, this);
12713         el.on("dblclick", this.onDblClick, this);
12714
12715         if(this.checkbox){
12716             Roo.EventManager.on(this.checkbox,
12717                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12718         }
12719
12720         el.on("contextmenu", this.onContextMenu, this);
12721
12722         var icon = Roo.fly(this.iconNode);
12723         icon.on("click", this.onClick, this);
12724         icon.on("dblclick", this.onDblClick, this);
12725         icon.on("contextmenu", this.onContextMenu, this);
12726         E.on(this.ecNode, "click", this.ecClick, this, true);
12727
12728         if(this.node.disabled){
12729             this.addClass("x-tree-node-disabled");
12730         }
12731         if(this.node.hidden){
12732             this.addClass("x-tree-node-disabled");
12733         }
12734         var ot = this.node.getOwnerTree();
12735         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12736         if(dd && (!this.node.isRoot || ot.rootVisible)){
12737             Roo.dd.Registry.register(this.elNode, {
12738                 node: this.node,
12739                 handles: this.getDDHandles(),
12740                 isHandle: false
12741             });
12742         }
12743     },
12744
12745     getDDHandles : function(){
12746         return [this.iconNode, this.textNode];
12747     },
12748
12749     hide : function(){
12750         if(this.rendered){
12751             this.wrap.style.display = "none";
12752         }
12753     },
12754
12755     show : function(){
12756         if(this.rendered){
12757             this.wrap.style.display = "";
12758         }
12759     },
12760
12761     onContextMenu : function(e){
12762         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12763             e.preventDefault();
12764             this.focus();
12765             this.fireEvent("contextmenu", this.node, e);
12766         }
12767     },
12768
12769     onClick : function(e){
12770         if(this.dropping){
12771             e.stopEvent();
12772             return;
12773         }
12774         if(this.fireEvent("beforeclick", this.node, e) !== false){
12775             if(!this.disabled && this.node.attributes.href){
12776                 this.fireEvent("click", this.node, e);
12777                 return;
12778             }
12779             e.preventDefault();
12780             if(this.disabled){
12781                 return;
12782             }
12783
12784             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12785                 this.node.toggle();
12786             }
12787
12788             this.fireEvent("click", this.node, e);
12789         }else{
12790             e.stopEvent();
12791         }
12792     },
12793
12794     onDblClick : function(e){
12795         e.preventDefault();
12796         if(this.disabled){
12797             return;
12798         }
12799         if(this.checkbox){
12800             this.toggleCheck();
12801         }
12802         if(!this.animating && this.node.hasChildNodes()){
12803             this.node.toggle();
12804         }
12805         this.fireEvent("dblclick", this.node, e);
12806     },
12807
12808     onCheckChange : function(){
12809         var checked = this.checkbox.checked;
12810         this.node.attributes.checked = checked;
12811         this.fireEvent('checkchange', this.node, checked);
12812     },
12813
12814     ecClick : function(e){
12815         if(!this.animating && this.node.hasChildNodes()){
12816             this.node.toggle();
12817         }
12818     },
12819
12820     startDrop : function(){
12821         this.dropping = true;
12822     },
12823
12824     // delayed drop so the click event doesn't get fired on a drop
12825     endDrop : function(){
12826        setTimeout(function(){
12827            this.dropping = false;
12828        }.createDelegate(this), 50);
12829     },
12830
12831     expand : function(){
12832         this.updateExpandIcon();
12833         this.ctNode.style.display = "";
12834     },
12835
12836     focus : function(){
12837         if(!this.node.preventHScroll){
12838             try{this.anchor.focus();
12839             }catch(e){}
12840         }else if(!Roo.isIE){
12841             try{
12842                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12843                 var l = noscroll.scrollLeft;
12844                 this.anchor.focus();
12845                 noscroll.scrollLeft = l;
12846             }catch(e){}
12847         }
12848     },
12849
12850     toggleCheck : function(value){
12851         var cb = this.checkbox;
12852         if(cb){
12853             cb.checked = (value === undefined ? !cb.checked : value);
12854         }
12855     },
12856
12857     blur : function(){
12858         try{
12859             this.anchor.blur();
12860         }catch(e){}
12861     },
12862
12863     animExpand : function(callback){
12864         var ct = Roo.get(this.ctNode);
12865         ct.stopFx();
12866         if(!this.node.hasChildNodes()){
12867             this.updateExpandIcon();
12868             this.ctNode.style.display = "";
12869             Roo.callback(callback);
12870             return;
12871         }
12872         this.animating = true;
12873         this.updateExpandIcon();
12874
12875         ct.slideIn('t', {
12876            callback : function(){
12877                this.animating = false;
12878                Roo.callback(callback);
12879             },
12880             scope: this,
12881             duration: this.node.ownerTree.duration || .25
12882         });
12883     },
12884
12885     highlight : function(){
12886         var tree = this.node.getOwnerTree();
12887         Roo.fly(this.wrap).highlight(
12888             tree.hlColor || "C3DAF9",
12889             {endColor: tree.hlBaseColor}
12890         );
12891     },
12892
12893     collapse : function(){
12894         this.updateExpandIcon();
12895         this.ctNode.style.display = "none";
12896     },
12897
12898     animCollapse : function(callback){
12899         var ct = Roo.get(this.ctNode);
12900         ct.enableDisplayMode('block');
12901         ct.stopFx();
12902
12903         this.animating = true;
12904         this.updateExpandIcon();
12905
12906         ct.slideOut('t', {
12907             callback : function(){
12908                this.animating = false;
12909                Roo.callback(callback);
12910             },
12911             scope: this,
12912             duration: this.node.ownerTree.duration || .25
12913         });
12914     },
12915
12916     getContainer : function(){
12917         return this.ctNode;
12918     },
12919
12920     getEl : function(){
12921         return this.wrap;
12922     },
12923
12924     appendDDGhost : function(ghostNode){
12925         ghostNode.appendChild(this.elNode.cloneNode(true));
12926     },
12927
12928     getDDRepairXY : function(){
12929         return Roo.lib.Dom.getXY(this.iconNode);
12930     },
12931
12932     onRender : function(){
12933         this.render();
12934     },
12935
12936     render : function(bulkRender){
12937         var n = this.node, a = n.attributes;
12938         var targetNode = n.parentNode ?
12939               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12940
12941         if(!this.rendered){
12942             this.rendered = true;
12943
12944             this.renderElements(n, a, targetNode, bulkRender);
12945
12946             if(a.qtip){
12947                if(this.textNode.setAttributeNS){
12948                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12949                    if(a.qtipTitle){
12950                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12951                    }
12952                }else{
12953                    this.textNode.setAttribute("ext:qtip", a.qtip);
12954                    if(a.qtipTitle){
12955                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12956                    }
12957                }
12958             }else if(a.qtipCfg){
12959                 a.qtipCfg.target = Roo.id(this.textNode);
12960                 Roo.QuickTips.register(a.qtipCfg);
12961             }
12962             this.initEvents();
12963             if(!this.node.expanded){
12964                 this.updateExpandIcon();
12965             }
12966         }else{
12967             if(bulkRender === true) {
12968                 targetNode.appendChild(this.wrap);
12969             }
12970         }
12971     },
12972
12973     renderElements : function(n, a, targetNode, bulkRender)
12974     {
12975         // add some indent caching, this helps performance when rendering a large tree
12976         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
12977         var t = n.getOwnerTree();
12978         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
12979         if (typeof(n.attributes.html) != 'undefined') {
12980             txt = n.attributes.html;
12981         }
12982         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
12983         var cb = typeof a.checked == 'boolean';
12984         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
12985         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
12986             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
12987             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
12988             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
12989             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
12990             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
12991              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
12992                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
12993             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
12994             "</li>"];
12995
12996         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
12997             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
12998                                 n.nextSibling.ui.getEl(), buf.join(""));
12999         }else{
13000             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13001         }
13002
13003         this.elNode = this.wrap.childNodes[0];
13004         this.ctNode = this.wrap.childNodes[1];
13005         var cs = this.elNode.childNodes;
13006         this.indentNode = cs[0];
13007         this.ecNode = cs[1];
13008         this.iconNode = cs[2];
13009         var index = 3;
13010         if(cb){
13011             this.checkbox = cs[3];
13012             index++;
13013         }
13014         this.anchor = cs[index];
13015         this.textNode = cs[index].firstChild;
13016     },
13017
13018     getAnchor : function(){
13019         return this.anchor;
13020     },
13021
13022     getTextEl : function(){
13023         return this.textNode;
13024     },
13025
13026     getIconEl : function(){
13027         return this.iconNode;
13028     },
13029
13030     isChecked : function(){
13031         return this.checkbox ? this.checkbox.checked : false;
13032     },
13033
13034     updateExpandIcon : function(){
13035         if(this.rendered){
13036             var n = this.node, c1, c2;
13037             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13038             var hasChild = n.hasChildNodes();
13039             if(hasChild){
13040                 if(n.expanded){
13041                     cls += "-minus";
13042                     c1 = "x-tree-node-collapsed";
13043                     c2 = "x-tree-node-expanded";
13044                 }else{
13045                     cls += "-plus";
13046                     c1 = "x-tree-node-expanded";
13047                     c2 = "x-tree-node-collapsed";
13048                 }
13049                 if(this.wasLeaf){
13050                     this.removeClass("x-tree-node-leaf");
13051                     this.wasLeaf = false;
13052                 }
13053                 if(this.c1 != c1 || this.c2 != c2){
13054                     Roo.fly(this.elNode).replaceClass(c1, c2);
13055                     this.c1 = c1; this.c2 = c2;
13056                 }
13057             }else{
13058                 // this changes non-leafs into leafs if they have no children.
13059                 // it's not very rational behaviour..
13060                 
13061                 if(!this.wasLeaf && this.node.leaf){
13062                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13063                     delete this.c1;
13064                     delete this.c2;
13065                     this.wasLeaf = true;
13066                 }
13067             }
13068             var ecc = "x-tree-ec-icon "+cls;
13069             if(this.ecc != ecc){
13070                 this.ecNode.className = ecc;
13071                 this.ecc = ecc;
13072             }
13073         }
13074     },
13075
13076     getChildIndent : function(){
13077         if(!this.childIndent){
13078             var buf = [];
13079             var p = this.node;
13080             while(p){
13081                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13082                     if(!p.isLast()) {
13083                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13084                     } else {
13085                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13086                     }
13087                 }
13088                 p = p.parentNode;
13089             }
13090             this.childIndent = buf.join("");
13091         }
13092         return this.childIndent;
13093     },
13094
13095     renderIndent : function(){
13096         if(this.rendered){
13097             var indent = "";
13098             var p = this.node.parentNode;
13099             if(p){
13100                 indent = p.ui.getChildIndent();
13101             }
13102             if(this.indentMarkup != indent){ // don't rerender if not required
13103                 this.indentNode.innerHTML = indent;
13104                 this.indentMarkup = indent;
13105             }
13106             this.updateExpandIcon();
13107         }
13108     }
13109 };
13110
13111 Roo.tree.RootTreeNodeUI = function(){
13112     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13113 };
13114 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13115     render : function(){
13116         if(!this.rendered){
13117             var targetNode = this.node.ownerTree.innerCt.dom;
13118             this.node.expanded = true;
13119             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13120             this.wrap = this.ctNode = targetNode.firstChild;
13121         }
13122     },
13123     collapse : function(){
13124     },
13125     expand : function(){
13126     }
13127 });/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137 /**
13138  * @class Roo.tree.TreeLoader
13139  * @extends Roo.util.Observable
13140  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13141  * nodes from a specified URL. The response must be a javascript Array definition
13142  * who's elements are node definition objects. eg:
13143  * <pre><code>
13144 {  success : true,
13145    data :      [
13146    
13147     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13148     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13149     ]
13150 }
13151
13152
13153 </code></pre>
13154  * <br><br>
13155  * The old style respose with just an array is still supported, but not recommended.
13156  * <br><br>
13157  *
13158  * A server request is sent, and child nodes are loaded only when a node is expanded.
13159  * The loading node's id is passed to the server under the parameter name "node" to
13160  * enable the server to produce the correct child nodes.
13161  * <br><br>
13162  * To pass extra parameters, an event handler may be attached to the "beforeload"
13163  * event, and the parameters specified in the TreeLoader's baseParams property:
13164  * <pre><code>
13165     myTreeLoader.on("beforeload", function(treeLoader, node) {
13166         this.baseParams.category = node.attributes.category;
13167     }, this);
13168     
13169 </code></pre>
13170  *
13171  * This would pass an HTTP parameter called "category" to the server containing
13172  * the value of the Node's "category" attribute.
13173  * @constructor
13174  * Creates a new Treeloader.
13175  * @param {Object} config A config object containing config properties.
13176  */
13177 Roo.tree.TreeLoader = function(config){
13178     this.baseParams = {};
13179     this.requestMethod = "POST";
13180     Roo.apply(this, config);
13181
13182     this.addEvents({
13183     
13184         /**
13185          * @event beforeload
13186          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13187          * @param {Object} This TreeLoader object.
13188          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13189          * @param {Object} callback The callback function specified in the {@link #load} call.
13190          */
13191         beforeload : true,
13192         /**
13193          * @event load
13194          * Fires when the node has been successfuly loaded.
13195          * @param {Object} This TreeLoader object.
13196          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13197          * @param {Object} response The response object containing the data from the server.
13198          */
13199         load : true,
13200         /**
13201          * @event loadexception
13202          * Fires if the network request failed.
13203          * @param {Object} This TreeLoader object.
13204          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13205          * @param {Object} response The response object containing the data from the server.
13206          */
13207         loadexception : true,
13208         /**
13209          * @event create
13210          * Fires before a node is created, enabling you to return custom Node types 
13211          * @param {Object} This TreeLoader object.
13212          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13213          */
13214         create : true
13215     });
13216
13217     Roo.tree.TreeLoader.superclass.constructor.call(this);
13218 };
13219
13220 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13221     /**
13222     * @cfg {String} dataUrl The URL from which to request a Json string which
13223     * specifies an array of node definition object representing the child nodes
13224     * to be loaded.
13225     */
13226     /**
13227     * @cfg {String} requestMethod either GET or POST
13228     * defaults to POST (due to BC)
13229     * to be loaded.
13230     */
13231     /**
13232     * @cfg {Object} baseParams (optional) An object containing properties which
13233     * specify HTTP parameters to be passed to each request for child nodes.
13234     */
13235     /**
13236     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13237     * created by this loader. If the attributes sent by the server have an attribute in this object,
13238     * they take priority.
13239     */
13240     /**
13241     * @cfg {Object} uiProviders (optional) An object containing properties which
13242     * 
13243     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13244     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13245     * <i>uiProvider</i> attribute of a returned child node is a string rather
13246     * than a reference to a TreeNodeUI implementation, this that string value
13247     * is used as a property name in the uiProviders object. You can define the provider named
13248     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13249     */
13250     uiProviders : {},
13251
13252     /**
13253     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13254     * child nodes before loading.
13255     */
13256     clearOnLoad : true,
13257
13258     /**
13259     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13260     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13261     * Grid query { data : [ .....] }
13262     */
13263     
13264     root : false,
13265      /**
13266     * @cfg {String} queryParam (optional) 
13267     * Name of the query as it will be passed on the querystring (defaults to 'node')
13268     * eg. the request will be ?node=[id]
13269     */
13270     
13271     
13272     queryParam: false,
13273     
13274     /**
13275      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13276      * This is called automatically when a node is expanded, but may be used to reload
13277      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13278      * @param {Roo.tree.TreeNode} node
13279      * @param {Function} callback
13280      */
13281     load : function(node, callback){
13282         if(this.clearOnLoad){
13283             while(node.firstChild){
13284                 node.removeChild(node.firstChild);
13285             }
13286         }
13287         if(node.attributes.children){ // preloaded json children
13288             var cs = node.attributes.children;
13289             for(var i = 0, len = cs.length; i < len; i++){
13290                 node.appendChild(this.createNode(cs[i]));
13291             }
13292             if(typeof callback == "function"){
13293                 callback();
13294             }
13295         }else if(this.dataUrl){
13296             this.requestData(node, callback);
13297         }
13298     },
13299
13300     getParams: function(node){
13301         var buf = [], bp = this.baseParams;
13302         for(var key in bp){
13303             if(typeof bp[key] != "function"){
13304                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13305             }
13306         }
13307         var n = this.queryParam === false ? 'node' : this.queryParam;
13308         buf.push(n + "=", encodeURIComponent(node.id));
13309         return buf.join("");
13310     },
13311
13312     requestData : function(node, callback){
13313         if(this.fireEvent("beforeload", this, node, callback) !== false){
13314             this.transId = Roo.Ajax.request({
13315                 method:this.requestMethod,
13316                 url: this.dataUrl||this.url,
13317                 success: this.handleResponse,
13318                 failure: this.handleFailure,
13319                 scope: this,
13320                 argument: {callback: callback, node: node},
13321                 params: this.getParams(node)
13322             });
13323         }else{
13324             // if the load is cancelled, make sure we notify
13325             // the node that we are done
13326             if(typeof callback == "function"){
13327                 callback();
13328             }
13329         }
13330     },
13331
13332     isLoading : function(){
13333         return this.transId ? true : false;
13334     },
13335
13336     abort : function(){
13337         if(this.isLoading()){
13338             Roo.Ajax.abort(this.transId);
13339         }
13340     },
13341
13342     // private
13343     createNode : function(attr)
13344     {
13345         // apply baseAttrs, nice idea Corey!
13346         if(this.baseAttrs){
13347             Roo.applyIf(attr, this.baseAttrs);
13348         }
13349         if(this.applyLoader !== false){
13350             attr.loader = this;
13351         }
13352         // uiProvider = depreciated..
13353         
13354         if(typeof(attr.uiProvider) == 'string'){
13355            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13356                 /**  eval:var:attr */ eval(attr.uiProvider);
13357         }
13358         if(typeof(this.uiProviders['default']) != 'undefined') {
13359             attr.uiProvider = this.uiProviders['default'];
13360         }
13361         
13362         this.fireEvent('create', this, attr);
13363         
13364         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13365         return(attr.leaf ?
13366                         new Roo.tree.TreeNode(attr) :
13367                         new Roo.tree.AsyncTreeNode(attr));
13368     },
13369
13370     processResponse : function(response, node, callback)
13371     {
13372         var json = response.responseText;
13373         try {
13374             
13375             var o = Roo.decode(json);
13376             
13377             if (this.root === false && typeof(o.success) != undefined) {
13378                 this.root = 'data'; // the default behaviour for list like data..
13379                 }
13380                 
13381             if (this.root !== false &&  !o.success) {
13382                 // it's a failure condition.
13383                 var a = response.argument;
13384                 this.fireEvent("loadexception", this, a.node, response);
13385                 Roo.log("Load failed - should have a handler really");
13386                 return;
13387             }
13388             
13389             
13390             
13391             if (this.root !== false) {
13392                  o = o[this.root];
13393             }
13394             
13395             for(var i = 0, len = o.length; i < len; i++){
13396                 var n = this.createNode(o[i]);
13397                 if(n){
13398                     node.appendChild(n);
13399                 }
13400             }
13401             if(typeof callback == "function"){
13402                 callback(this, node);
13403             }
13404         }catch(e){
13405             this.handleFailure(response);
13406         }
13407     },
13408
13409     handleResponse : function(response){
13410         this.transId = false;
13411         var a = response.argument;
13412         this.processResponse(response, a.node, a.callback);
13413         this.fireEvent("load", this, a.node, response);
13414     },
13415
13416     handleFailure : function(response)
13417     {
13418         // should handle failure better..
13419         this.transId = false;
13420         var a = response.argument;
13421         this.fireEvent("loadexception", this, a.node, response);
13422         if(typeof a.callback == "function"){
13423             a.callback(this, a.node);
13424         }
13425     }
13426 });/*
13427  * Based on:
13428  * Ext JS Library 1.1.1
13429  * Copyright(c) 2006-2007, Ext JS, LLC.
13430  *
13431  * Originally Released Under LGPL - original licence link has changed is not relivant.
13432  *
13433  * Fork - LGPL
13434  * <script type="text/javascript">
13435  */
13436
13437 /**
13438 * @class Roo.tree.TreeFilter
13439 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13440 * @param {TreePanel} tree
13441 * @param {Object} config (optional)
13442  */
13443 Roo.tree.TreeFilter = function(tree, config){
13444     this.tree = tree;
13445     this.filtered = {};
13446     Roo.apply(this, config);
13447 };
13448
13449 Roo.tree.TreeFilter.prototype = {
13450     clearBlank:false,
13451     reverse:false,
13452     autoClear:false,
13453     remove:false,
13454
13455      /**
13456      * Filter the data by a specific attribute.
13457      * @param {String/RegExp} value Either string that the attribute value
13458      * should start with or a RegExp to test against the attribute
13459      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13460      * @param {TreeNode} startNode (optional) The node to start the filter at.
13461      */
13462     filter : function(value, attr, startNode){
13463         attr = attr || "text";
13464         var f;
13465         if(typeof value == "string"){
13466             var vlen = value.length;
13467             // auto clear empty filter
13468             if(vlen == 0 && this.clearBlank){
13469                 this.clear();
13470                 return;
13471             }
13472             value = value.toLowerCase();
13473             f = function(n){
13474                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13475             };
13476         }else if(value.exec){ // regex?
13477             f = function(n){
13478                 return value.test(n.attributes[attr]);
13479             };
13480         }else{
13481             throw 'Illegal filter type, must be string or regex';
13482         }
13483         this.filterBy(f, null, startNode);
13484         },
13485
13486     /**
13487      * Filter by a function. The passed function will be called with each
13488      * node in the tree (or from the startNode). If the function returns true, the node is kept
13489      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13490      * @param {Function} fn The filter function
13491      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13492      */
13493     filterBy : function(fn, scope, startNode){
13494         startNode = startNode || this.tree.root;
13495         if(this.autoClear){
13496             this.clear();
13497         }
13498         var af = this.filtered, rv = this.reverse;
13499         var f = function(n){
13500             if(n == startNode){
13501                 return true;
13502             }
13503             if(af[n.id]){
13504                 return false;
13505             }
13506             var m = fn.call(scope || n, n);
13507             if(!m || rv){
13508                 af[n.id] = n;
13509                 n.ui.hide();
13510                 return false;
13511             }
13512             return true;
13513         };
13514         startNode.cascade(f);
13515         if(this.remove){
13516            for(var id in af){
13517                if(typeof id != "function"){
13518                    var n = af[id];
13519                    if(n && n.parentNode){
13520                        n.parentNode.removeChild(n);
13521                    }
13522                }
13523            }
13524         }
13525     },
13526
13527     /**
13528      * Clears the current filter. Note: with the "remove" option
13529      * set a filter cannot be cleared.
13530      */
13531     clear : function(){
13532         var t = this.tree;
13533         var af = this.filtered;
13534         for(var id in af){
13535             if(typeof id != "function"){
13536                 var n = af[id];
13537                 if(n){
13538                     n.ui.show();
13539                 }
13540             }
13541         }
13542         this.filtered = {};
13543     }
13544 };
13545 /*
13546  * Based on:
13547  * Ext JS Library 1.1.1
13548  * Copyright(c) 2006-2007, Ext JS, LLC.
13549  *
13550  * Originally Released Under LGPL - original licence link has changed is not relivant.
13551  *
13552  * Fork - LGPL
13553  * <script type="text/javascript">
13554  */
13555  
13556
13557 /**
13558  * @class Roo.tree.TreeSorter
13559  * Provides sorting of nodes in a TreePanel
13560  * 
13561  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13562  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13563  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13564  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13565  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13566  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13567  * @constructor
13568  * @param {TreePanel} tree
13569  * @param {Object} config
13570  */
13571 Roo.tree.TreeSorter = function(tree, config){
13572     Roo.apply(this, config);
13573     tree.on("beforechildrenrendered", this.doSort, this);
13574     tree.on("append", this.updateSort, this);
13575     tree.on("insert", this.updateSort, this);
13576     
13577     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13578     var p = this.property || "text";
13579     var sortType = this.sortType;
13580     var fs = this.folderSort;
13581     var cs = this.caseSensitive === true;
13582     var leafAttr = this.leafAttr || 'leaf';
13583
13584     this.sortFn = function(n1, n2){
13585         if(fs){
13586             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13587                 return 1;
13588             }
13589             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13590                 return -1;
13591             }
13592         }
13593         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13594         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13595         if(v1 < v2){
13596                         return dsc ? +1 : -1;
13597                 }else if(v1 > v2){
13598                         return dsc ? -1 : +1;
13599         }else{
13600                 return 0;
13601         }
13602     };
13603 };
13604
13605 Roo.tree.TreeSorter.prototype = {
13606     doSort : function(node){
13607         node.sort(this.sortFn);
13608     },
13609     
13610     compareNodes : function(n1, n2){
13611         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13612     },
13613     
13614     updateSort : function(tree, node){
13615         if(node.childrenRendered){
13616             this.doSort.defer(1, this, [node]);
13617         }
13618     }
13619 };/*
13620  * Based on:
13621  * Ext JS Library 1.1.1
13622  * Copyright(c) 2006-2007, Ext JS, LLC.
13623  *
13624  * Originally Released Under LGPL - original licence link has changed is not relivant.
13625  *
13626  * Fork - LGPL
13627  * <script type="text/javascript">
13628  */
13629
13630 if(Roo.dd.DropZone){
13631     
13632 Roo.tree.TreeDropZone = function(tree, config){
13633     this.allowParentInsert = false;
13634     this.allowContainerDrop = false;
13635     this.appendOnly = false;
13636     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13637     this.tree = tree;
13638     this.lastInsertClass = "x-tree-no-status";
13639     this.dragOverData = {};
13640 };
13641
13642 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13643     ddGroup : "TreeDD",
13644     scroll:  true,
13645     
13646     expandDelay : 1000,
13647     
13648     expandNode : function(node){
13649         if(node.hasChildNodes() && !node.isExpanded()){
13650             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13651         }
13652     },
13653     
13654     queueExpand : function(node){
13655         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13656     },
13657     
13658     cancelExpand : function(){
13659         if(this.expandProcId){
13660             clearTimeout(this.expandProcId);
13661             this.expandProcId = false;
13662         }
13663     },
13664     
13665     isValidDropPoint : function(n, pt, dd, e, data){
13666         if(!n || !data){ return false; }
13667         var targetNode = n.node;
13668         var dropNode = data.node;
13669         // default drop rules
13670         if(!(targetNode && targetNode.isTarget && pt)){
13671             return false;
13672         }
13673         if(pt == "append" && targetNode.allowChildren === false){
13674             return false;
13675         }
13676         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13677             return false;
13678         }
13679         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13680             return false;
13681         }
13682         // reuse the object
13683         var overEvent = this.dragOverData;
13684         overEvent.tree = this.tree;
13685         overEvent.target = targetNode;
13686         overEvent.data = data;
13687         overEvent.point = pt;
13688         overEvent.source = dd;
13689         overEvent.rawEvent = e;
13690         overEvent.dropNode = dropNode;
13691         overEvent.cancel = false;  
13692         var result = this.tree.fireEvent("nodedragover", overEvent);
13693         return overEvent.cancel === false && result !== false;
13694     },
13695     
13696     getDropPoint : function(e, n, dd)
13697     {
13698         var tn = n.node;
13699         if(tn.isRoot){
13700             return tn.allowChildren !== false ? "append" : false; // always append for root
13701         }
13702         var dragEl = n.ddel;
13703         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13704         var y = Roo.lib.Event.getPageY(e);
13705         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13706         
13707         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13708         var noAppend = tn.allowChildren === false;
13709         if(this.appendOnly || tn.parentNode.allowChildren === false){
13710             return noAppend ? false : "append";
13711         }
13712         var noBelow = false;
13713         if(!this.allowParentInsert){
13714             noBelow = tn.hasChildNodes() && tn.isExpanded();
13715         }
13716         var q = (b - t) / (noAppend ? 2 : 3);
13717         if(y >= t && y < (t + q)){
13718             return "above";
13719         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13720             return "below";
13721         }else{
13722             return "append";
13723         }
13724     },
13725     
13726     onNodeEnter : function(n, dd, e, data)
13727     {
13728         this.cancelExpand();
13729     },
13730     
13731     onNodeOver : function(n, dd, e, data)
13732     {
13733        
13734         var pt = this.getDropPoint(e, n, dd);
13735         var node = n.node;
13736         
13737         // auto node expand check
13738         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13739             this.queueExpand(node);
13740         }else if(pt != "append"){
13741             this.cancelExpand();
13742         }
13743         
13744         // set the insert point style on the target node
13745         var returnCls = this.dropNotAllowed;
13746         if(this.isValidDropPoint(n, pt, dd, e, data)){
13747            if(pt){
13748                var el = n.ddel;
13749                var cls;
13750                if(pt == "above"){
13751                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13752                    cls = "x-tree-drag-insert-above";
13753                }else if(pt == "below"){
13754                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13755                    cls = "x-tree-drag-insert-below";
13756                }else{
13757                    returnCls = "x-tree-drop-ok-append";
13758                    cls = "x-tree-drag-append";
13759                }
13760                if(this.lastInsertClass != cls){
13761                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13762                    this.lastInsertClass = cls;
13763                }
13764            }
13765        }
13766        return returnCls;
13767     },
13768     
13769     onNodeOut : function(n, dd, e, data){
13770         
13771         this.cancelExpand();
13772         this.removeDropIndicators(n);
13773     },
13774     
13775     onNodeDrop : function(n, dd, e, data){
13776         var point = this.getDropPoint(e, n, dd);
13777         var targetNode = n.node;
13778         targetNode.ui.startDrop();
13779         if(!this.isValidDropPoint(n, point, dd, e, data)){
13780             targetNode.ui.endDrop();
13781             return false;
13782         }
13783         // first try to find the drop node
13784         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13785         var dropEvent = {
13786             tree : this.tree,
13787             target: targetNode,
13788             data: data,
13789             point: point,
13790             source: dd,
13791             rawEvent: e,
13792             dropNode: dropNode,
13793             cancel: !dropNode   
13794         };
13795         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13796         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13797             targetNode.ui.endDrop();
13798             return false;
13799         }
13800         // allow target changing
13801         targetNode = dropEvent.target;
13802         if(point == "append" && !targetNode.isExpanded()){
13803             targetNode.expand(false, null, function(){
13804                 this.completeDrop(dropEvent);
13805             }.createDelegate(this));
13806         }else{
13807             this.completeDrop(dropEvent);
13808         }
13809         return true;
13810     },
13811     
13812     completeDrop : function(de){
13813         var ns = de.dropNode, p = de.point, t = de.target;
13814         if(!(ns instanceof Array)){
13815             ns = [ns];
13816         }
13817         var n;
13818         for(var i = 0, len = ns.length; i < len; i++){
13819             n = ns[i];
13820             if(p == "above"){
13821                 t.parentNode.insertBefore(n, t);
13822             }else if(p == "below"){
13823                 t.parentNode.insertBefore(n, t.nextSibling);
13824             }else{
13825                 t.appendChild(n);
13826             }
13827         }
13828         n.ui.focus();
13829         if(this.tree.hlDrop){
13830             n.ui.highlight();
13831         }
13832         t.ui.endDrop();
13833         this.tree.fireEvent("nodedrop", de);
13834     },
13835     
13836     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13837         if(this.tree.hlDrop){
13838             dropNode.ui.focus();
13839             dropNode.ui.highlight();
13840         }
13841         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13842     },
13843     
13844     getTree : function(){
13845         return this.tree;
13846     },
13847     
13848     removeDropIndicators : function(n){
13849         if(n && n.ddel){
13850             var el = n.ddel;
13851             Roo.fly(el).removeClass([
13852                     "x-tree-drag-insert-above",
13853                     "x-tree-drag-insert-below",
13854                     "x-tree-drag-append"]);
13855             this.lastInsertClass = "_noclass";
13856         }
13857     },
13858     
13859     beforeDragDrop : function(target, e, id){
13860         this.cancelExpand();
13861         return true;
13862     },
13863     
13864     afterRepair : function(data){
13865         if(data && Roo.enableFx){
13866             data.node.ui.highlight();
13867         }
13868         this.hideProxy();
13869     } 
13870     
13871 });
13872
13873 }
13874 /*
13875  * Based on:
13876  * Ext JS Library 1.1.1
13877  * Copyright(c) 2006-2007, Ext JS, LLC.
13878  *
13879  * Originally Released Under LGPL - original licence link has changed is not relivant.
13880  *
13881  * Fork - LGPL
13882  * <script type="text/javascript">
13883  */
13884  
13885
13886 if(Roo.dd.DragZone){
13887 Roo.tree.TreeDragZone = function(tree, config){
13888     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13889     this.tree = tree;
13890 };
13891
13892 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13893     ddGroup : "TreeDD",
13894    
13895     onBeforeDrag : function(data, e){
13896         var n = data.node;
13897         return n && n.draggable && !n.disabled;
13898     },
13899      
13900     
13901     onInitDrag : function(e){
13902         var data = this.dragData;
13903         this.tree.getSelectionModel().select(data.node);
13904         this.proxy.update("");
13905         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13906         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13907     },
13908     
13909     getRepairXY : function(e, data){
13910         return data.node.ui.getDDRepairXY();
13911     },
13912     
13913     onEndDrag : function(data, e){
13914         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13915         
13916         
13917     },
13918     
13919     onValidDrop : function(dd, e, id){
13920         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13921         this.hideProxy();
13922     },
13923     
13924     beforeInvalidDrop : function(e, id){
13925         // this scrolls the original position back into view
13926         var sm = this.tree.getSelectionModel();
13927         sm.clearSelections();
13928         sm.select(this.dragData.node);
13929     }
13930 });
13931 }/*
13932  * Based on:
13933  * Ext JS Library 1.1.1
13934  * Copyright(c) 2006-2007, Ext JS, LLC.
13935  *
13936  * Originally Released Under LGPL - original licence link has changed is not relivant.
13937  *
13938  * Fork - LGPL
13939  * <script type="text/javascript">
13940  */
13941 /**
13942  * @class Roo.tree.TreeEditor
13943  * @extends Roo.Editor
13944  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13945  * as the editor field.
13946  * @constructor
13947  * @param {Object} config (used to be the tree panel.)
13948  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13949  * 
13950  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13951  * @cfg {Roo.form.TextField|Object} field The field configuration
13952  *
13953  * 
13954  */
13955 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13956     var tree = config;
13957     var field;
13958     if (oldconfig) { // old style..
13959         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13960     } else {
13961         // new style..
13962         tree = config.tree;
13963         config.field = config.field  || {};
13964         config.field.xtype = 'TextField';
13965         field = Roo.factory(config.field, Roo.form);
13966     }
13967     config = config || {};
13968     
13969     
13970     this.addEvents({
13971         /**
13972          * @event beforenodeedit
13973          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13974          * false from the handler of this event.
13975          * @param {Editor} this
13976          * @param {Roo.tree.Node} node 
13977          */
13978         "beforenodeedit" : true
13979     });
13980     
13981     //Roo.log(config);
13982     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
13983
13984     this.tree = tree;
13985
13986     tree.on('beforeclick', this.beforeNodeClick, this);
13987     tree.getTreeEl().on('mousedown', this.hide, this);
13988     this.on('complete', this.updateNode, this);
13989     this.on('beforestartedit', this.fitToTree, this);
13990     this.on('startedit', this.bindScroll, this, {delay:10});
13991     this.on('specialkey', this.onSpecialKey, this);
13992 };
13993
13994 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
13995     /**
13996      * @cfg {String} alignment
13997      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
13998      */
13999     alignment: "l-l",
14000     // inherit
14001     autoSize: false,
14002     /**
14003      * @cfg {Boolean} hideEl
14004      * True to hide the bound element while the editor is displayed (defaults to false)
14005      */
14006     hideEl : false,
14007     /**
14008      * @cfg {String} cls
14009      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14010      */
14011     cls: "x-small-editor x-tree-editor",
14012     /**
14013      * @cfg {Boolean} shim
14014      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14015      */
14016     shim:false,
14017     // inherit
14018     shadow:"frame",
14019     /**
14020      * @cfg {Number} maxWidth
14021      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14022      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14023      * scroll and client offsets into account prior to each edit.
14024      */
14025     maxWidth: 250,
14026
14027     editDelay : 350,
14028
14029     // private
14030     fitToTree : function(ed, el){
14031         var td = this.tree.getTreeEl().dom, nd = el.dom;
14032         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14033             td.scrollLeft = nd.offsetLeft;
14034         }
14035         var w = Math.min(
14036                 this.maxWidth,
14037                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14038         this.setSize(w, '');
14039         
14040         return this.fireEvent('beforenodeedit', this, this.editNode);
14041         
14042     },
14043
14044     // private
14045     triggerEdit : function(node){
14046         this.completeEdit();
14047         this.editNode = node;
14048         this.startEdit(node.ui.textNode, node.text);
14049     },
14050
14051     // private
14052     bindScroll : function(){
14053         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14054     },
14055
14056     // private
14057     beforeNodeClick : function(node, e){
14058         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14059         this.lastClick = new Date();
14060         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14061             e.stopEvent();
14062             this.triggerEdit(node);
14063             return false;
14064         }
14065         return true;
14066     },
14067
14068     // private
14069     updateNode : function(ed, value){
14070         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14071         this.editNode.setText(value);
14072     },
14073
14074     // private
14075     onHide : function(){
14076         Roo.tree.TreeEditor.superclass.onHide.call(this);
14077         if(this.editNode){
14078             this.editNode.ui.focus();
14079         }
14080     },
14081
14082     // private
14083     onSpecialKey : function(field, e){
14084         var k = e.getKey();
14085         if(k == e.ESC){
14086             e.stopEvent();
14087             this.cancelEdit();
14088         }else if(k == e.ENTER && !e.hasModifier()){
14089             e.stopEvent();
14090             this.completeEdit();
14091         }
14092     }
14093 });//<Script type="text/javascript">
14094 /*
14095  * Based on:
14096  * Ext JS Library 1.1.1
14097  * Copyright(c) 2006-2007, Ext JS, LLC.
14098  *
14099  * Originally Released Under LGPL - original licence link has changed is not relivant.
14100  *
14101  * Fork - LGPL
14102  * <script type="text/javascript">
14103  */
14104  
14105 /**
14106  * Not documented??? - probably should be...
14107  */
14108
14109 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14110     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14111     
14112     renderElements : function(n, a, targetNode, bulkRender){
14113         //consel.log("renderElements?");
14114         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14115
14116         var t = n.getOwnerTree();
14117         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14118         
14119         var cols = t.columns;
14120         var bw = t.borderWidth;
14121         var c = cols[0];
14122         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14123          var cb = typeof a.checked == "boolean";
14124         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14125         var colcls = 'x-t-' + tid + '-c0';
14126         var buf = [
14127             '<li class="x-tree-node">',
14128             
14129                 
14130                 '<div class="x-tree-node-el ', a.cls,'">',
14131                     // extran...
14132                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14133                 
14134                 
14135                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14136                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14137                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14138                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14139                            (a.iconCls ? ' '+a.iconCls : ''),
14140                            '" unselectable="on" />',
14141                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14142                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14143                              
14144                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14145                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14146                             '<span unselectable="on" qtip="' + tx + '">',
14147                              tx,
14148                              '</span></a>' ,
14149                     '</div>',
14150                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14151                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14152                  ];
14153         for(var i = 1, len = cols.length; i < len; i++){
14154             c = cols[i];
14155             colcls = 'x-t-' + tid + '-c' +i;
14156             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14157             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14158                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14159                       "</div>");
14160          }
14161          
14162          buf.push(
14163             '</a>',
14164             '<div class="x-clear"></div></div>',
14165             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14166             "</li>");
14167         
14168         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14169             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14170                                 n.nextSibling.ui.getEl(), buf.join(""));
14171         }else{
14172             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14173         }
14174         var el = this.wrap.firstChild;
14175         this.elRow = el;
14176         this.elNode = el.firstChild;
14177         this.ranchor = el.childNodes[1];
14178         this.ctNode = this.wrap.childNodes[1];
14179         var cs = el.firstChild.childNodes;
14180         this.indentNode = cs[0];
14181         this.ecNode = cs[1];
14182         this.iconNode = cs[2];
14183         var index = 3;
14184         if(cb){
14185             this.checkbox = cs[3];
14186             index++;
14187         }
14188         this.anchor = cs[index];
14189         
14190         this.textNode = cs[index].firstChild;
14191         
14192         //el.on("click", this.onClick, this);
14193         //el.on("dblclick", this.onDblClick, this);
14194         
14195         
14196        // console.log(this);
14197     },
14198     initEvents : function(){
14199         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14200         
14201             
14202         var a = this.ranchor;
14203
14204         var el = Roo.get(a);
14205
14206         if(Roo.isOpera){ // opera render bug ignores the CSS
14207             el.setStyle("text-decoration", "none");
14208         }
14209
14210         el.on("click", this.onClick, this);
14211         el.on("dblclick", this.onDblClick, this);
14212         el.on("contextmenu", this.onContextMenu, this);
14213         
14214     },
14215     
14216     /*onSelectedChange : function(state){
14217         if(state){
14218             this.focus();
14219             this.addClass("x-tree-selected");
14220         }else{
14221             //this.blur();
14222             this.removeClass("x-tree-selected");
14223         }
14224     },*/
14225     addClass : function(cls){
14226         if(this.elRow){
14227             Roo.fly(this.elRow).addClass(cls);
14228         }
14229         
14230     },
14231     
14232     
14233     removeClass : function(cls){
14234         if(this.elRow){
14235             Roo.fly(this.elRow).removeClass(cls);
14236         }
14237     }
14238
14239     
14240     
14241 });//<Script type="text/javascript">
14242
14243 /*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253  
14254
14255 /**
14256  * @class Roo.tree.ColumnTree
14257  * @extends Roo.data.TreePanel
14258  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14259  * @cfg {int} borderWidth  compined right/left border allowance
14260  * @constructor
14261  * @param {String/HTMLElement/Element} el The container element
14262  * @param {Object} config
14263  */
14264 Roo.tree.ColumnTree =  function(el, config)
14265 {
14266    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14267    this.addEvents({
14268         /**
14269         * @event resize
14270         * Fire this event on a container when it resizes
14271         * @param {int} w Width
14272         * @param {int} h Height
14273         */
14274        "resize" : true
14275     });
14276     this.on('resize', this.onResize, this);
14277 };
14278
14279 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14280     //lines:false,
14281     
14282     
14283     borderWidth: Roo.isBorderBox ? 0 : 2, 
14284     headEls : false,
14285     
14286     render : function(){
14287         // add the header.....
14288        
14289         Roo.tree.ColumnTree.superclass.render.apply(this);
14290         
14291         this.el.addClass('x-column-tree');
14292         
14293         this.headers = this.el.createChild(
14294             {cls:'x-tree-headers'},this.innerCt.dom);
14295    
14296         var cols = this.columns, c;
14297         var totalWidth = 0;
14298         this.headEls = [];
14299         var  len = cols.length;
14300         for(var i = 0; i < len; i++){
14301              c = cols[i];
14302              totalWidth += c.width;
14303             this.headEls.push(this.headers.createChild({
14304                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14305                  cn: {
14306                      cls:'x-tree-hd-text',
14307                      html: c.header
14308                  },
14309                  style:'width:'+(c.width-this.borderWidth)+'px;'
14310              }));
14311         }
14312         this.headers.createChild({cls:'x-clear'});
14313         // prevent floats from wrapping when clipped
14314         this.headers.setWidth(totalWidth);
14315         //this.innerCt.setWidth(totalWidth);
14316         this.innerCt.setStyle({ overflow: 'auto' });
14317         this.onResize(this.width, this.height);
14318              
14319         
14320     },
14321     onResize : function(w,h)
14322     {
14323         this.height = h;
14324         this.width = w;
14325         // resize cols..
14326         this.innerCt.setWidth(this.width);
14327         this.innerCt.setHeight(this.height-20);
14328         
14329         // headers...
14330         var cols = this.columns, c;
14331         var totalWidth = 0;
14332         var expEl = false;
14333         var len = cols.length;
14334         for(var i = 0; i < len; i++){
14335             c = cols[i];
14336             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14337                 // it's the expander..
14338                 expEl  = this.headEls[i];
14339                 continue;
14340             }
14341             totalWidth += c.width;
14342             
14343         }
14344         if (expEl) {
14345             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14346         }
14347         this.headers.setWidth(w-20);
14348
14349         
14350         
14351         
14352     }
14353 });
14354 /*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364  
14365 /**
14366  * @class Roo.menu.Menu
14367  * @extends Roo.util.Observable
14368  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14369  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14370  * @constructor
14371  * Creates a new Menu
14372  * @param {Object} config Configuration options
14373  */
14374 Roo.menu.Menu = function(config){
14375     
14376     Roo.menu.Menu.superclass.constructor.call(this, config);
14377     
14378     this.id = this.id || Roo.id();
14379     this.addEvents({
14380         /**
14381          * @event beforeshow
14382          * Fires before this menu is displayed
14383          * @param {Roo.menu.Menu} this
14384          */
14385         beforeshow : true,
14386         /**
14387          * @event beforehide
14388          * Fires before this menu is hidden
14389          * @param {Roo.menu.Menu} this
14390          */
14391         beforehide : true,
14392         /**
14393          * @event show
14394          * Fires after this menu is displayed
14395          * @param {Roo.menu.Menu} this
14396          */
14397         show : true,
14398         /**
14399          * @event hide
14400          * Fires after this menu is hidden
14401          * @param {Roo.menu.Menu} this
14402          */
14403         hide : true,
14404         /**
14405          * @event click
14406          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14407          * @param {Roo.menu.Menu} this
14408          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14409          * @param {Roo.EventObject} e
14410          */
14411         click : true,
14412         /**
14413          * @event mouseover
14414          * Fires when the mouse is hovering over this menu
14415          * @param {Roo.menu.Menu} this
14416          * @param {Roo.EventObject} e
14417          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14418          */
14419         mouseover : true,
14420         /**
14421          * @event mouseout
14422          * Fires when the mouse exits this menu
14423          * @param {Roo.menu.Menu} this
14424          * @param {Roo.EventObject} e
14425          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14426          */
14427         mouseout : true,
14428         /**
14429          * @event itemclick
14430          * Fires when a menu item contained in this menu is clicked
14431          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14432          * @param {Roo.EventObject} e
14433          */
14434         itemclick: true
14435     });
14436     if (this.registerMenu) {
14437         Roo.menu.MenuMgr.register(this);
14438     }
14439     
14440     var mis = this.items;
14441     this.items = new Roo.util.MixedCollection();
14442     if(mis){
14443         this.add.apply(this, mis);
14444     }
14445 };
14446
14447 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14448     /**
14449      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14450      */
14451     minWidth : 120,
14452     /**
14453      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14454      * for bottom-right shadow (defaults to "sides")
14455      */
14456     shadow : "sides",
14457     /**
14458      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14459      * this menu (defaults to "tl-tr?")
14460      */
14461     subMenuAlign : "tl-tr?",
14462     /**
14463      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14464      * relative to its element of origin (defaults to "tl-bl?")
14465      */
14466     defaultAlign : "tl-bl?",
14467     /**
14468      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14469      */
14470     allowOtherMenus : false,
14471     /**
14472      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14473      */
14474     registerMenu : true,
14475
14476     hidden:true,
14477
14478     // private
14479     render : function(){
14480         if(this.el){
14481             return;
14482         }
14483         var el = this.el = new Roo.Layer({
14484             cls: "x-menu",
14485             shadow:this.shadow,
14486             constrain: false,
14487             parentEl: this.parentEl || document.body,
14488             zindex:15000
14489         });
14490
14491         this.keyNav = new Roo.menu.MenuNav(this);
14492
14493         if(this.plain){
14494             el.addClass("x-menu-plain");
14495         }
14496         if(this.cls){
14497             el.addClass(this.cls);
14498         }
14499         // generic focus element
14500         this.focusEl = el.createChild({
14501             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14502         });
14503         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14504         //disabling touch- as it's causing issues ..
14505         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14506         ul.on('click'   , this.onClick, this);
14507         
14508         
14509         ul.on("mouseover", this.onMouseOver, this);
14510         ul.on("mouseout", this.onMouseOut, this);
14511         this.items.each(function(item){
14512             if (item.hidden) {
14513                 return;
14514             }
14515             
14516             var li = document.createElement("li");
14517             li.className = "x-menu-list-item";
14518             ul.dom.appendChild(li);
14519             item.render(li, this);
14520         }, this);
14521         this.ul = ul;
14522         this.autoWidth();
14523     },
14524
14525     // private
14526     autoWidth : function(){
14527         var el = this.el, ul = this.ul;
14528         if(!el){
14529             return;
14530         }
14531         var w = this.width;
14532         if(w){
14533             el.setWidth(w);
14534         }else if(Roo.isIE){
14535             el.setWidth(this.minWidth);
14536             var t = el.dom.offsetWidth; // force recalc
14537             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14538         }
14539     },
14540
14541     // private
14542     delayAutoWidth : function(){
14543         if(this.rendered){
14544             if(!this.awTask){
14545                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14546             }
14547             this.awTask.delay(20);
14548         }
14549     },
14550
14551     // private
14552     findTargetItem : function(e){
14553         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14554         if(t && t.menuItemId){
14555             return this.items.get(t.menuItemId);
14556         }
14557     },
14558
14559     // private
14560     onClick : function(e){
14561         Roo.log("menu.onClick");
14562         var t = this.findTargetItem(e);
14563         if(!t){
14564             return;
14565         }
14566         Roo.log(e);
14567         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14568             if(t == this.activeItem && t.shouldDeactivate(e)){
14569                 this.activeItem.deactivate();
14570                 delete this.activeItem;
14571                 return;
14572             }
14573             if(t.canActivate){
14574                 this.setActiveItem(t, true);
14575             }
14576             return;
14577             
14578             
14579         }
14580         
14581         t.onClick(e);
14582         this.fireEvent("click", this, t, e);
14583     },
14584
14585     // private
14586     setActiveItem : function(item, autoExpand){
14587         if(item != this.activeItem){
14588             if(this.activeItem){
14589                 this.activeItem.deactivate();
14590             }
14591             this.activeItem = item;
14592             item.activate(autoExpand);
14593         }else if(autoExpand){
14594             item.expandMenu();
14595         }
14596     },
14597
14598     // private
14599     tryActivate : function(start, step){
14600         var items = this.items;
14601         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14602             var item = items.get(i);
14603             if(!item.disabled && item.canActivate){
14604                 this.setActiveItem(item, false);
14605                 return item;
14606             }
14607         }
14608         return false;
14609     },
14610
14611     // private
14612     onMouseOver : function(e){
14613         var t;
14614         if(t = this.findTargetItem(e)){
14615             if(t.canActivate && !t.disabled){
14616                 this.setActiveItem(t, true);
14617             }
14618         }
14619         this.fireEvent("mouseover", this, e, t);
14620     },
14621
14622     // private
14623     onMouseOut : function(e){
14624         var t;
14625         if(t = this.findTargetItem(e)){
14626             if(t == this.activeItem && t.shouldDeactivate(e)){
14627                 this.activeItem.deactivate();
14628                 delete this.activeItem;
14629             }
14630         }
14631         this.fireEvent("mouseout", this, e, t);
14632     },
14633
14634     /**
14635      * Read-only.  Returns true if the menu is currently displayed, else false.
14636      * @type Boolean
14637      */
14638     isVisible : function(){
14639         return this.el && !this.hidden;
14640     },
14641
14642     /**
14643      * Displays this menu relative to another element
14644      * @param {String/HTMLElement/Roo.Element} element The element to align to
14645      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14646      * the element (defaults to this.defaultAlign)
14647      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14648      */
14649     show : function(el, pos, parentMenu){
14650         this.parentMenu = parentMenu;
14651         if(!this.el){
14652             this.render();
14653         }
14654         this.fireEvent("beforeshow", this);
14655         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14656     },
14657
14658     /**
14659      * Displays this menu at a specific xy position
14660      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14661      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14662      */
14663     showAt : function(xy, parentMenu, /* private: */_e){
14664         this.parentMenu = parentMenu;
14665         if(!this.el){
14666             this.render();
14667         }
14668         if(_e !== false){
14669             this.fireEvent("beforeshow", this);
14670             xy = this.el.adjustForConstraints(xy);
14671         }
14672         this.el.setXY(xy);
14673         this.el.show();
14674         this.hidden = false;
14675         this.focus();
14676         this.fireEvent("show", this);
14677     },
14678
14679     focus : function(){
14680         if(!this.hidden){
14681             this.doFocus.defer(50, this);
14682         }
14683     },
14684
14685     doFocus : function(){
14686         if(!this.hidden){
14687             this.focusEl.focus();
14688         }
14689     },
14690
14691     /**
14692      * Hides this menu and optionally all parent menus
14693      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14694      */
14695     hide : function(deep){
14696         if(this.el && this.isVisible()){
14697             this.fireEvent("beforehide", this);
14698             if(this.activeItem){
14699                 this.activeItem.deactivate();
14700                 this.activeItem = null;
14701             }
14702             this.el.hide();
14703             this.hidden = true;
14704             this.fireEvent("hide", this);
14705         }
14706         if(deep === true && this.parentMenu){
14707             this.parentMenu.hide(true);
14708         }
14709     },
14710
14711     /**
14712      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14713      * Any of the following are valid:
14714      * <ul>
14715      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14716      * <li>An HTMLElement object which will be converted to a menu item</li>
14717      * <li>A menu item config object that will be created as a new menu item</li>
14718      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14719      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14720      * </ul>
14721      * Usage:
14722      * <pre><code>
14723 // Create the menu
14724 var menu = new Roo.menu.Menu();
14725
14726 // Create a menu item to add by reference
14727 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14728
14729 // Add a bunch of items at once using different methods.
14730 // Only the last item added will be returned.
14731 var item = menu.add(
14732     menuItem,                // add existing item by ref
14733     'Dynamic Item',          // new TextItem
14734     '-',                     // new separator
14735     { text: 'Config Item' }  // new item by config
14736 );
14737 </code></pre>
14738      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14739      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14740      */
14741     add : function(){
14742         var a = arguments, l = a.length, item;
14743         for(var i = 0; i < l; i++){
14744             var el = a[i];
14745             if ((typeof(el) == "object") && el.xtype && el.xns) {
14746                 el = Roo.factory(el, Roo.menu);
14747             }
14748             
14749             if(el.render){ // some kind of Item
14750                 item = this.addItem(el);
14751             }else if(typeof el == "string"){ // string
14752                 if(el == "separator" || el == "-"){
14753                     item = this.addSeparator();
14754                 }else{
14755                     item = this.addText(el);
14756                 }
14757             }else if(el.tagName || el.el){ // element
14758                 item = this.addElement(el);
14759             }else if(typeof el == "object"){ // must be menu item config?
14760                 item = this.addMenuItem(el);
14761             }
14762         }
14763         return item;
14764     },
14765
14766     /**
14767      * Returns this menu's underlying {@link Roo.Element} object
14768      * @return {Roo.Element} The element
14769      */
14770     getEl : function(){
14771         if(!this.el){
14772             this.render();
14773         }
14774         return this.el;
14775     },
14776
14777     /**
14778      * Adds a separator bar to the menu
14779      * @return {Roo.menu.Item} The menu item that was added
14780      */
14781     addSeparator : function(){
14782         return this.addItem(new Roo.menu.Separator());
14783     },
14784
14785     /**
14786      * Adds an {@link Roo.Element} object to the menu
14787      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14788      * @return {Roo.menu.Item} The menu item that was added
14789      */
14790     addElement : function(el){
14791         return this.addItem(new Roo.menu.BaseItem(el));
14792     },
14793
14794     /**
14795      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14796      * @param {Roo.menu.Item} item The menu item to add
14797      * @return {Roo.menu.Item} The menu item that was added
14798      */
14799     addItem : function(item){
14800         this.items.add(item);
14801         if(this.ul){
14802             var li = document.createElement("li");
14803             li.className = "x-menu-list-item";
14804             this.ul.dom.appendChild(li);
14805             item.render(li, this);
14806             this.delayAutoWidth();
14807         }
14808         return item;
14809     },
14810
14811     /**
14812      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14813      * @param {Object} config A MenuItem config object
14814      * @return {Roo.menu.Item} The menu item that was added
14815      */
14816     addMenuItem : function(config){
14817         if(!(config instanceof Roo.menu.Item)){
14818             if(typeof config.checked == "boolean"){ // must be check menu item config?
14819                 config = new Roo.menu.CheckItem(config);
14820             }else{
14821                 config = new Roo.menu.Item(config);
14822             }
14823         }
14824         return this.addItem(config);
14825     },
14826
14827     /**
14828      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14829      * @param {String} text The text to display in the menu item
14830      * @return {Roo.menu.Item} The menu item that was added
14831      */
14832     addText : function(text){
14833         return this.addItem(new Roo.menu.TextItem({ text : text }));
14834     },
14835
14836     /**
14837      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14838      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14839      * @param {Roo.menu.Item} item The menu item to add
14840      * @return {Roo.menu.Item} The menu item that was added
14841      */
14842     insert : function(index, item){
14843         this.items.insert(index, item);
14844         if(this.ul){
14845             var li = document.createElement("li");
14846             li.className = "x-menu-list-item";
14847             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14848             item.render(li, this);
14849             this.delayAutoWidth();
14850         }
14851         return item;
14852     },
14853
14854     /**
14855      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14856      * @param {Roo.menu.Item} item The menu item to remove
14857      */
14858     remove : function(item){
14859         this.items.removeKey(item.id);
14860         item.destroy();
14861     },
14862
14863     /**
14864      * Removes and destroys all items in the menu
14865      */
14866     removeAll : function(){
14867         var f;
14868         while(f = this.items.first()){
14869             this.remove(f);
14870         }
14871     }
14872 });
14873
14874 // MenuNav is a private utility class used internally by the Menu
14875 Roo.menu.MenuNav = function(menu){
14876     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14877     this.scope = this.menu = menu;
14878 };
14879
14880 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14881     doRelay : function(e, h){
14882         var k = e.getKey();
14883         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14884             this.menu.tryActivate(0, 1);
14885             return false;
14886         }
14887         return h.call(this.scope || this, e, this.menu);
14888     },
14889
14890     up : function(e, m){
14891         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14892             m.tryActivate(m.items.length-1, -1);
14893         }
14894     },
14895
14896     down : function(e, m){
14897         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14898             m.tryActivate(0, 1);
14899         }
14900     },
14901
14902     right : function(e, m){
14903         if(m.activeItem){
14904             m.activeItem.expandMenu(true);
14905         }
14906     },
14907
14908     left : function(e, m){
14909         m.hide();
14910         if(m.parentMenu && m.parentMenu.activeItem){
14911             m.parentMenu.activeItem.activate();
14912         }
14913     },
14914
14915     enter : function(e, m){
14916         if(m.activeItem){
14917             e.stopPropagation();
14918             m.activeItem.onClick(e);
14919             m.fireEvent("click", this, m.activeItem);
14920             return true;
14921         }
14922     }
14923 });/*
14924  * Based on:
14925  * Ext JS Library 1.1.1
14926  * Copyright(c) 2006-2007, Ext JS, LLC.
14927  *
14928  * Originally Released Under LGPL - original licence link has changed is not relivant.
14929  *
14930  * Fork - LGPL
14931  * <script type="text/javascript">
14932  */
14933  
14934 /**
14935  * @class Roo.menu.MenuMgr
14936  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14937  * @singleton
14938  */
14939 Roo.menu.MenuMgr = function(){
14940    var menus, active, groups = {}, attached = false, lastShow = new Date();
14941
14942    // private - called when first menu is created
14943    function init(){
14944        menus = {};
14945        active = new Roo.util.MixedCollection();
14946        Roo.get(document).addKeyListener(27, function(){
14947            if(active.length > 0){
14948                hideAll();
14949            }
14950        });
14951    }
14952
14953    // private
14954    function hideAll(){
14955        if(active && active.length > 0){
14956            var c = active.clone();
14957            c.each(function(m){
14958                m.hide();
14959            });
14960        }
14961    }
14962
14963    // private
14964    function onHide(m){
14965        active.remove(m);
14966        if(active.length < 1){
14967            Roo.get(document).un("mousedown", onMouseDown);
14968            attached = false;
14969        }
14970    }
14971
14972    // private
14973    function onShow(m){
14974        var last = active.last();
14975        lastShow = new Date();
14976        active.add(m);
14977        if(!attached){
14978            Roo.get(document).on("mousedown", onMouseDown);
14979            attached = true;
14980        }
14981        if(m.parentMenu){
14982           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
14983           m.parentMenu.activeChild = m;
14984        }else if(last && last.isVisible()){
14985           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
14986        }
14987    }
14988
14989    // private
14990    function onBeforeHide(m){
14991        if(m.activeChild){
14992            m.activeChild.hide();
14993        }
14994        if(m.autoHideTimer){
14995            clearTimeout(m.autoHideTimer);
14996            delete m.autoHideTimer;
14997        }
14998    }
14999
15000    // private
15001    function onBeforeShow(m){
15002        var pm = m.parentMenu;
15003        if(!pm && !m.allowOtherMenus){
15004            hideAll();
15005        }else if(pm && pm.activeChild && active != m){
15006            pm.activeChild.hide();
15007        }
15008    }
15009
15010    // private
15011    function onMouseDown(e){
15012        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15013            hideAll();
15014        }
15015    }
15016
15017    // private
15018    function onBeforeCheck(mi, state){
15019        if(state){
15020            var g = groups[mi.group];
15021            for(var i = 0, l = g.length; i < l; i++){
15022                if(g[i] != mi){
15023                    g[i].setChecked(false);
15024                }
15025            }
15026        }
15027    }
15028
15029    return {
15030
15031        /**
15032         * Hides all menus that are currently visible
15033         */
15034        hideAll : function(){
15035             hideAll();  
15036        },
15037
15038        // private
15039        register : function(menu){
15040            if(!menus){
15041                init();
15042            }
15043            menus[menu.id] = menu;
15044            menu.on("beforehide", onBeforeHide);
15045            menu.on("hide", onHide);
15046            menu.on("beforeshow", onBeforeShow);
15047            menu.on("show", onShow);
15048            var g = menu.group;
15049            if(g && menu.events["checkchange"]){
15050                if(!groups[g]){
15051                    groups[g] = [];
15052                }
15053                groups[g].push(menu);
15054                menu.on("checkchange", onCheck);
15055            }
15056        },
15057
15058         /**
15059          * Returns a {@link Roo.menu.Menu} object
15060          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15061          * be used to generate and return a new Menu instance.
15062          */
15063        get : function(menu){
15064            if(typeof menu == "string"){ // menu id
15065                return menus[menu];
15066            }else if(menu.events){  // menu instance
15067                return menu;
15068            }else if(typeof menu.length == 'number'){ // array of menu items?
15069                return new Roo.menu.Menu({items:menu});
15070            }else{ // otherwise, must be a config
15071                return new Roo.menu.Menu(menu);
15072            }
15073        },
15074
15075        // private
15076        unregister : function(menu){
15077            delete menus[menu.id];
15078            menu.un("beforehide", onBeforeHide);
15079            menu.un("hide", onHide);
15080            menu.un("beforeshow", onBeforeShow);
15081            menu.un("show", onShow);
15082            var g = menu.group;
15083            if(g && menu.events["checkchange"]){
15084                groups[g].remove(menu);
15085                menu.un("checkchange", onCheck);
15086            }
15087        },
15088
15089        // private
15090        registerCheckable : function(menuItem){
15091            var g = menuItem.group;
15092            if(g){
15093                if(!groups[g]){
15094                    groups[g] = [];
15095                }
15096                groups[g].push(menuItem);
15097                menuItem.on("beforecheckchange", onBeforeCheck);
15098            }
15099        },
15100
15101        // private
15102        unregisterCheckable : function(menuItem){
15103            var g = menuItem.group;
15104            if(g){
15105                groups[g].remove(menuItem);
15106                menuItem.un("beforecheckchange", onBeforeCheck);
15107            }
15108        }
15109    };
15110 }();/*
15111  * Based on:
15112  * Ext JS Library 1.1.1
15113  * Copyright(c) 2006-2007, Ext JS, LLC.
15114  *
15115  * Originally Released Under LGPL - original licence link has changed is not relivant.
15116  *
15117  * Fork - LGPL
15118  * <script type="text/javascript">
15119  */
15120  
15121
15122 /**
15123  * @class Roo.menu.BaseItem
15124  * @extends Roo.Component
15125  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15126  * management and base configuration options shared by all menu components.
15127  * @constructor
15128  * Creates a new BaseItem
15129  * @param {Object} config Configuration options
15130  */
15131 Roo.menu.BaseItem = function(config){
15132     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15133
15134     this.addEvents({
15135         /**
15136          * @event click
15137          * Fires when this item is clicked
15138          * @param {Roo.menu.BaseItem} this
15139          * @param {Roo.EventObject} e
15140          */
15141         click: true,
15142         /**
15143          * @event activate
15144          * Fires when this item is activated
15145          * @param {Roo.menu.BaseItem} this
15146          */
15147         activate : true,
15148         /**
15149          * @event deactivate
15150          * Fires when this item is deactivated
15151          * @param {Roo.menu.BaseItem} this
15152          */
15153         deactivate : true
15154     });
15155
15156     if(this.handler){
15157         this.on("click", this.handler, this.scope, true);
15158     }
15159 };
15160
15161 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15162     /**
15163      * @cfg {Function} handler
15164      * A function that will handle the click event of this menu item (defaults to undefined)
15165      */
15166     /**
15167      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15168      */
15169     canActivate : false,
15170     
15171      /**
15172      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15173      */
15174     hidden: false,
15175     
15176     /**
15177      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15178      */
15179     activeClass : "x-menu-item-active",
15180     /**
15181      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15182      */
15183     hideOnClick : true,
15184     /**
15185      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15186      */
15187     hideDelay : 100,
15188
15189     // private
15190     ctype: "Roo.menu.BaseItem",
15191
15192     // private
15193     actionMode : "container",
15194
15195     // private
15196     render : function(container, parentMenu){
15197         this.parentMenu = parentMenu;
15198         Roo.menu.BaseItem.superclass.render.call(this, container);
15199         this.container.menuItemId = this.id;
15200     },
15201
15202     // private
15203     onRender : function(container, position){
15204         this.el = Roo.get(this.el);
15205         container.dom.appendChild(this.el.dom);
15206     },
15207
15208     // private
15209     onClick : function(e){
15210         if(!this.disabled && this.fireEvent("click", this, e) !== false
15211                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15212             this.handleClick(e);
15213         }else{
15214             e.stopEvent();
15215         }
15216     },
15217
15218     // private
15219     activate : function(){
15220         if(this.disabled){
15221             return false;
15222         }
15223         var li = this.container;
15224         li.addClass(this.activeClass);
15225         this.region = li.getRegion().adjust(2, 2, -2, -2);
15226         this.fireEvent("activate", this);
15227         return true;
15228     },
15229
15230     // private
15231     deactivate : function(){
15232         this.container.removeClass(this.activeClass);
15233         this.fireEvent("deactivate", this);
15234     },
15235
15236     // private
15237     shouldDeactivate : function(e){
15238         return !this.region || !this.region.contains(e.getPoint());
15239     },
15240
15241     // private
15242     handleClick : function(e){
15243         if(this.hideOnClick){
15244             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15245         }
15246     },
15247
15248     // private
15249     expandMenu : function(autoActivate){
15250         // do nothing
15251     },
15252
15253     // private
15254     hideMenu : function(){
15255         // do nothing
15256     }
15257 });/*
15258  * Based on:
15259  * Ext JS Library 1.1.1
15260  * Copyright(c) 2006-2007, Ext JS, LLC.
15261  *
15262  * Originally Released Under LGPL - original licence link has changed is not relivant.
15263  *
15264  * Fork - LGPL
15265  * <script type="text/javascript">
15266  */
15267  
15268 /**
15269  * @class Roo.menu.Adapter
15270  * @extends Roo.menu.BaseItem
15271  * 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.
15272  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15273  * @constructor
15274  * Creates a new Adapter
15275  * @param {Object} config Configuration options
15276  */
15277 Roo.menu.Adapter = function(component, config){
15278     Roo.menu.Adapter.superclass.constructor.call(this, config);
15279     this.component = component;
15280 };
15281 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15282     // private
15283     canActivate : true,
15284
15285     // private
15286     onRender : function(container, position){
15287         this.component.render(container);
15288         this.el = this.component.getEl();
15289     },
15290
15291     // private
15292     activate : function(){
15293         if(this.disabled){
15294             return false;
15295         }
15296         this.component.focus();
15297         this.fireEvent("activate", this);
15298         return true;
15299     },
15300
15301     // private
15302     deactivate : function(){
15303         this.fireEvent("deactivate", this);
15304     },
15305
15306     // private
15307     disable : function(){
15308         this.component.disable();
15309         Roo.menu.Adapter.superclass.disable.call(this);
15310     },
15311
15312     // private
15313     enable : function(){
15314         this.component.enable();
15315         Roo.menu.Adapter.superclass.enable.call(this);
15316     }
15317 });/*
15318  * Based on:
15319  * Ext JS Library 1.1.1
15320  * Copyright(c) 2006-2007, Ext JS, LLC.
15321  *
15322  * Originally Released Under LGPL - original licence link has changed is not relivant.
15323  *
15324  * Fork - LGPL
15325  * <script type="text/javascript">
15326  */
15327
15328 /**
15329  * @class Roo.menu.TextItem
15330  * @extends Roo.menu.BaseItem
15331  * Adds a static text string to a menu, usually used as either a heading or group separator.
15332  * Note: old style constructor with text is still supported.
15333  * 
15334  * @constructor
15335  * Creates a new TextItem
15336  * @param {Object} cfg Configuration
15337  */
15338 Roo.menu.TextItem = function(cfg){
15339     if (typeof(cfg) == 'string') {
15340         this.text = cfg;
15341     } else {
15342         Roo.apply(this,cfg);
15343     }
15344     
15345     Roo.menu.TextItem.superclass.constructor.call(this);
15346 };
15347
15348 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15349     /**
15350      * @cfg {String} text Text to show on item.
15351      */
15352     text : '',
15353     
15354     /**
15355      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15356      */
15357     hideOnClick : false,
15358     /**
15359      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15360      */
15361     itemCls : "x-menu-text",
15362
15363     // private
15364     onRender : function(){
15365         var s = document.createElement("span");
15366         s.className = this.itemCls;
15367         s.innerHTML = this.text;
15368         this.el = s;
15369         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15370     }
15371 });/*
15372  * Based on:
15373  * Ext JS Library 1.1.1
15374  * Copyright(c) 2006-2007, Ext JS, LLC.
15375  *
15376  * Originally Released Under LGPL - original licence link has changed is not relivant.
15377  *
15378  * Fork - LGPL
15379  * <script type="text/javascript">
15380  */
15381
15382 /**
15383  * @class Roo.menu.Separator
15384  * @extends Roo.menu.BaseItem
15385  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15386  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15387  * @constructor
15388  * @param {Object} config Configuration options
15389  */
15390 Roo.menu.Separator = function(config){
15391     Roo.menu.Separator.superclass.constructor.call(this, config);
15392 };
15393
15394 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15395     /**
15396      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15397      */
15398     itemCls : "x-menu-sep",
15399     /**
15400      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15401      */
15402     hideOnClick : false,
15403
15404     // private
15405     onRender : function(li){
15406         var s = document.createElement("span");
15407         s.className = this.itemCls;
15408         s.innerHTML = "&#160;";
15409         this.el = s;
15410         li.addClass("x-menu-sep-li");
15411         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15412     }
15413 });/*
15414  * Based on:
15415  * Ext JS Library 1.1.1
15416  * Copyright(c) 2006-2007, Ext JS, LLC.
15417  *
15418  * Originally Released Under LGPL - original licence link has changed is not relivant.
15419  *
15420  * Fork - LGPL
15421  * <script type="text/javascript">
15422  */
15423 /**
15424  * @class Roo.menu.Item
15425  * @extends Roo.menu.BaseItem
15426  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15427  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15428  * activation and click handling.
15429  * @constructor
15430  * Creates a new Item
15431  * @param {Object} config Configuration options
15432  */
15433 Roo.menu.Item = function(config){
15434     Roo.menu.Item.superclass.constructor.call(this, config);
15435     if(this.menu){
15436         this.menu = Roo.menu.MenuMgr.get(this.menu);
15437     }
15438 };
15439 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15440     
15441     /**
15442      * @cfg {String} text
15443      * The text to show on the menu item.
15444      */
15445     text: '',
15446      /**
15447      * @cfg {String} HTML to render in menu
15448      * The text to show on the menu item (HTML version).
15449      */
15450     html: '',
15451     /**
15452      * @cfg {String} icon
15453      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15454      */
15455     icon: undefined,
15456     /**
15457      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15458      */
15459     itemCls : "x-menu-item",
15460     /**
15461      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15462      */
15463     canActivate : true,
15464     /**
15465      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15466      */
15467     showDelay: 200,
15468     // doc'd in BaseItem
15469     hideDelay: 200,
15470
15471     // private
15472     ctype: "Roo.menu.Item",
15473     
15474     // private
15475     onRender : function(container, position){
15476         var el = document.createElement("a");
15477         el.hideFocus = true;
15478         el.unselectable = "on";
15479         el.href = this.href || "#";
15480         if(this.hrefTarget){
15481             el.target = this.hrefTarget;
15482         }
15483         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15484         
15485         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15486         
15487         el.innerHTML = String.format(
15488                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15489                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15490         this.el = el;
15491         Roo.menu.Item.superclass.onRender.call(this, container, position);
15492     },
15493
15494     /**
15495      * Sets the text to display in this menu item
15496      * @param {String} text The text to display
15497      * @param {Boolean} isHTML true to indicate text is pure html.
15498      */
15499     setText : function(text, isHTML){
15500         if (isHTML) {
15501             this.html = text;
15502         } else {
15503             this.text = text;
15504             this.html = '';
15505         }
15506         if(this.rendered){
15507             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15508      
15509             this.el.update(String.format(
15510                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15511                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15512             this.parentMenu.autoWidth();
15513         }
15514     },
15515
15516     // private
15517     handleClick : function(e){
15518         if(!this.href){ // if no link defined, stop the event automatically
15519             e.stopEvent();
15520         }
15521         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15522     },
15523
15524     // private
15525     activate : function(autoExpand){
15526         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15527             this.focus();
15528             if(autoExpand){
15529                 this.expandMenu();
15530             }
15531         }
15532         return true;
15533     },
15534
15535     // private
15536     shouldDeactivate : function(e){
15537         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15538             if(this.menu && this.menu.isVisible()){
15539                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15540             }
15541             return true;
15542         }
15543         return false;
15544     },
15545
15546     // private
15547     deactivate : function(){
15548         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15549         this.hideMenu();
15550     },
15551
15552     // private
15553     expandMenu : function(autoActivate){
15554         if(!this.disabled && this.menu){
15555             clearTimeout(this.hideTimer);
15556             delete this.hideTimer;
15557             if(!this.menu.isVisible() && !this.showTimer){
15558                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15559             }else if (this.menu.isVisible() && autoActivate){
15560                 this.menu.tryActivate(0, 1);
15561             }
15562         }
15563     },
15564
15565     // private
15566     deferExpand : function(autoActivate){
15567         delete this.showTimer;
15568         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15569         if(autoActivate){
15570             this.menu.tryActivate(0, 1);
15571         }
15572     },
15573
15574     // private
15575     hideMenu : function(){
15576         clearTimeout(this.showTimer);
15577         delete this.showTimer;
15578         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15579             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15580         }
15581     },
15582
15583     // private
15584     deferHide : function(){
15585         delete this.hideTimer;
15586         this.menu.hide();
15587     }
15588 });/*
15589  * Based on:
15590  * Ext JS Library 1.1.1
15591  * Copyright(c) 2006-2007, Ext JS, LLC.
15592  *
15593  * Originally Released Under LGPL - original licence link has changed is not relivant.
15594  *
15595  * Fork - LGPL
15596  * <script type="text/javascript">
15597  */
15598  
15599 /**
15600  * @class Roo.menu.CheckItem
15601  * @extends Roo.menu.Item
15602  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15603  * @constructor
15604  * Creates a new CheckItem
15605  * @param {Object} config Configuration options
15606  */
15607 Roo.menu.CheckItem = function(config){
15608     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15609     this.addEvents({
15610         /**
15611          * @event beforecheckchange
15612          * Fires before the checked value is set, providing an opportunity to cancel if needed
15613          * @param {Roo.menu.CheckItem} this
15614          * @param {Boolean} checked The new checked value that will be set
15615          */
15616         "beforecheckchange" : true,
15617         /**
15618          * @event checkchange
15619          * Fires after the checked value has been set
15620          * @param {Roo.menu.CheckItem} this
15621          * @param {Boolean} checked The checked value that was set
15622          */
15623         "checkchange" : true
15624     });
15625     if(this.checkHandler){
15626         this.on('checkchange', this.checkHandler, this.scope);
15627     }
15628 };
15629 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15630     /**
15631      * @cfg {String} group
15632      * All check items with the same group name will automatically be grouped into a single-select
15633      * radio button group (defaults to '')
15634      */
15635     /**
15636      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15637      */
15638     itemCls : "x-menu-item x-menu-check-item",
15639     /**
15640      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15641      */
15642     groupClass : "x-menu-group-item",
15643
15644     /**
15645      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15646      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15647      * initialized with checked = true will be rendered as checked.
15648      */
15649     checked: false,
15650
15651     // private
15652     ctype: "Roo.menu.CheckItem",
15653
15654     // private
15655     onRender : function(c){
15656         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15657         if(this.group){
15658             this.el.addClass(this.groupClass);
15659         }
15660         Roo.menu.MenuMgr.registerCheckable(this);
15661         if(this.checked){
15662             this.checked = false;
15663             this.setChecked(true, true);
15664         }
15665     },
15666
15667     // private
15668     destroy : function(){
15669         if(this.rendered){
15670             Roo.menu.MenuMgr.unregisterCheckable(this);
15671         }
15672         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15673     },
15674
15675     /**
15676      * Set the checked state of this item
15677      * @param {Boolean} checked The new checked value
15678      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15679      */
15680     setChecked : function(state, suppressEvent){
15681         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15682             if(this.container){
15683                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15684             }
15685             this.checked = state;
15686             if(suppressEvent !== true){
15687                 this.fireEvent("checkchange", this, state);
15688             }
15689         }
15690     },
15691
15692     // private
15693     handleClick : function(e){
15694        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15695            this.setChecked(!this.checked);
15696        }
15697        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15698     }
15699 });/*
15700  * Based on:
15701  * Ext JS Library 1.1.1
15702  * Copyright(c) 2006-2007, Ext JS, LLC.
15703  *
15704  * Originally Released Under LGPL - original licence link has changed is not relivant.
15705  *
15706  * Fork - LGPL
15707  * <script type="text/javascript">
15708  */
15709  
15710 /**
15711  * @class Roo.menu.DateItem
15712  * @extends Roo.menu.Adapter
15713  * A menu item that wraps the {@link Roo.DatPicker} component.
15714  * @constructor
15715  * Creates a new DateItem
15716  * @param {Object} config Configuration options
15717  */
15718 Roo.menu.DateItem = function(config){
15719     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15720     /** The Roo.DatePicker object @type Roo.DatePicker */
15721     this.picker = this.component;
15722     this.addEvents({select: true});
15723     
15724     this.picker.on("render", function(picker){
15725         picker.getEl().swallowEvent("click");
15726         picker.container.addClass("x-menu-date-item");
15727     });
15728
15729     this.picker.on("select", this.onSelect, this);
15730 };
15731
15732 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15733     // private
15734     onSelect : function(picker, date){
15735         this.fireEvent("select", this, date, picker);
15736         Roo.menu.DateItem.superclass.handleClick.call(this);
15737     }
15738 });/*
15739  * Based on:
15740  * Ext JS Library 1.1.1
15741  * Copyright(c) 2006-2007, Ext JS, LLC.
15742  *
15743  * Originally Released Under LGPL - original licence link has changed is not relivant.
15744  *
15745  * Fork - LGPL
15746  * <script type="text/javascript">
15747  */
15748  
15749 /**
15750  * @class Roo.menu.ColorItem
15751  * @extends Roo.menu.Adapter
15752  * A menu item that wraps the {@link Roo.ColorPalette} component.
15753  * @constructor
15754  * Creates a new ColorItem
15755  * @param {Object} config Configuration options
15756  */
15757 Roo.menu.ColorItem = function(config){
15758     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15759     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15760     this.palette = this.component;
15761     this.relayEvents(this.palette, ["select"]);
15762     if(this.selectHandler){
15763         this.on('select', this.selectHandler, this.scope);
15764     }
15765 };
15766 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15767  * Based on:
15768  * Ext JS Library 1.1.1
15769  * Copyright(c) 2006-2007, Ext JS, LLC.
15770  *
15771  * Originally Released Under LGPL - original licence link has changed is not relivant.
15772  *
15773  * Fork - LGPL
15774  * <script type="text/javascript">
15775  */
15776  
15777
15778 /**
15779  * @class Roo.menu.DateMenu
15780  * @extends Roo.menu.Menu
15781  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15782  * @constructor
15783  * Creates a new DateMenu
15784  * @param {Object} config Configuration options
15785  */
15786 Roo.menu.DateMenu = function(config){
15787     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15788     this.plain = true;
15789     var di = new Roo.menu.DateItem(config);
15790     this.add(di);
15791     /**
15792      * The {@link Roo.DatePicker} instance for this DateMenu
15793      * @type DatePicker
15794      */
15795     this.picker = di.picker;
15796     /**
15797      * @event select
15798      * @param {DatePicker} picker
15799      * @param {Date} date
15800      */
15801     this.relayEvents(di, ["select"]);
15802     this.on('beforeshow', function(){
15803         if(this.picker){
15804             this.picker.hideMonthPicker(false);
15805         }
15806     }, this);
15807 };
15808 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15809     cls:'x-date-menu'
15810 });/*
15811  * Based on:
15812  * Ext JS Library 1.1.1
15813  * Copyright(c) 2006-2007, Ext JS, LLC.
15814  *
15815  * Originally Released Under LGPL - original licence link has changed is not relivant.
15816  *
15817  * Fork - LGPL
15818  * <script type="text/javascript">
15819  */
15820  
15821
15822 /**
15823  * @class Roo.menu.ColorMenu
15824  * @extends Roo.menu.Menu
15825  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15826  * @constructor
15827  * Creates a new ColorMenu
15828  * @param {Object} config Configuration options
15829  */
15830 Roo.menu.ColorMenu = function(config){
15831     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15832     this.plain = true;
15833     var ci = new Roo.menu.ColorItem(config);
15834     this.add(ci);
15835     /**
15836      * The {@link Roo.ColorPalette} instance for this ColorMenu
15837      * @type ColorPalette
15838      */
15839     this.palette = ci.palette;
15840     /**
15841      * @event select
15842      * @param {ColorPalette} palette
15843      * @param {String} color
15844      */
15845     this.relayEvents(ci, ["select"]);
15846 };
15847 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15848  * Based on:
15849  * Ext JS Library 1.1.1
15850  * Copyright(c) 2006-2007, Ext JS, LLC.
15851  *
15852  * Originally Released Under LGPL - original licence link has changed is not relivant.
15853  *
15854  * Fork - LGPL
15855  * <script type="text/javascript">
15856  */
15857  
15858 /**
15859  * @class Roo.form.TextItem
15860  * @extends Roo.BoxComponent
15861  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15862  * @constructor
15863  * Creates a new TextItem
15864  * @param {Object} config Configuration options
15865  */
15866 Roo.form.TextItem = function(config){
15867     Roo.form.TextItem.superclass.constructor.call(this, config);
15868 };
15869
15870 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15871     
15872     /**
15873      * @cfg {String} tag the tag for this item (default div)
15874      */
15875     tag : 'div',
15876     /**
15877      * @cfg {String} html the content for this item
15878      */
15879     html : '',
15880     
15881     getAutoCreate : function()
15882     {
15883         var cfg = {
15884             id: this.id,
15885             tag: this.tag,
15886             html: this.html,
15887             cls: 'x-form-item'
15888         };
15889         
15890         return cfg;
15891         
15892     },
15893     
15894     onRender : function(ct, position)
15895     {
15896         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15897         
15898         if(!this.el){
15899             var cfg = this.getAutoCreate();
15900             if(!cfg.name){
15901                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15902             }
15903             if (!cfg.name.length) {
15904                 delete cfg.name;
15905             }
15906             this.el = ct.createChild(cfg, position);
15907         }
15908     },
15909     /*
15910      * setHTML
15911      * @param {String} html update the Contents of the element.
15912      */
15913     setHTML : function(html)
15914     {
15915         this.fieldEl.dom.innerHTML = html;
15916     }
15917     
15918 });/*
15919  * Based on:
15920  * Ext JS Library 1.1.1
15921  * Copyright(c) 2006-2007, Ext JS, LLC.
15922  *
15923  * Originally Released Under LGPL - original licence link has changed is not relivant.
15924  *
15925  * Fork - LGPL
15926  * <script type="text/javascript">
15927  */
15928  
15929 /**
15930  * @class Roo.form.Field
15931  * @extends Roo.BoxComponent
15932  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15933  * @constructor
15934  * Creates a new Field
15935  * @param {Object} config Configuration options
15936  */
15937 Roo.form.Field = function(config){
15938     Roo.form.Field.superclass.constructor.call(this, config);
15939 };
15940
15941 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15942     /**
15943      * @cfg {String} fieldLabel Label to use when rendering a form.
15944      */
15945        /**
15946      * @cfg {String} qtip Mouse over tip
15947      */
15948      
15949     /**
15950      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15951      */
15952     invalidClass : "x-form-invalid",
15953     /**
15954      * @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")
15955      */
15956     invalidText : "The value in this field is invalid",
15957     /**
15958      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15959      */
15960     focusClass : "x-form-focus",
15961     /**
15962      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15963       automatic validation (defaults to "keyup").
15964      */
15965     validationEvent : "keyup",
15966     /**
15967      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
15968      */
15969     validateOnBlur : true,
15970     /**
15971      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
15972      */
15973     validationDelay : 250,
15974     /**
15975      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
15976      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
15977      */
15978     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
15979     /**
15980      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
15981      */
15982     fieldClass : "x-form-field",
15983     /**
15984      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
15985      *<pre>
15986 Value         Description
15987 -----------   ----------------------------------------------------------------------
15988 qtip          Display a quick tip when the user hovers over the field
15989 title         Display a default browser title attribute popup
15990 under         Add a block div beneath the field containing the error text
15991 side          Add an error icon to the right of the field with a popup on hover
15992 [element id]  Add the error text directly to the innerHTML of the specified element
15993 </pre>
15994      */
15995     msgTarget : 'qtip',
15996     /**
15997      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
15998      */
15999     msgFx : 'normal',
16000
16001     /**
16002      * @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.
16003      */
16004     readOnly : false,
16005
16006     /**
16007      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16008      */
16009     disabled : false,
16010
16011     /**
16012      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16013      */
16014     inputType : undefined,
16015     
16016     /**
16017      * @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).
16018          */
16019         tabIndex : undefined,
16020         
16021     // private
16022     isFormField : true,
16023
16024     // private
16025     hasFocus : false,
16026     /**
16027      * @property {Roo.Element} fieldEl
16028      * Element Containing the rendered Field (with label etc.)
16029      */
16030     /**
16031      * @cfg {Mixed} value A value to initialize this field with.
16032      */
16033     value : undefined,
16034
16035     /**
16036      * @cfg {String} name The field's HTML name attribute.
16037      */
16038     /**
16039      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16040      */
16041     // private
16042     loadedValue : false,
16043      
16044      
16045         // private ??
16046         initComponent : function(){
16047         Roo.form.Field.superclass.initComponent.call(this);
16048         this.addEvents({
16049             /**
16050              * @event focus
16051              * Fires when this field receives input focus.
16052              * @param {Roo.form.Field} this
16053              */
16054             focus : true,
16055             /**
16056              * @event blur
16057              * Fires when this field loses input focus.
16058              * @param {Roo.form.Field} this
16059              */
16060             blur : true,
16061             /**
16062              * @event specialkey
16063              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16064              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16065              * @param {Roo.form.Field} this
16066              * @param {Roo.EventObject} e The event object
16067              */
16068             specialkey : true,
16069             /**
16070              * @event change
16071              * Fires just before the field blurs if the field value has changed.
16072              * @param {Roo.form.Field} this
16073              * @param {Mixed} newValue The new value
16074              * @param {Mixed} oldValue The original value
16075              */
16076             change : true,
16077             /**
16078              * @event invalid
16079              * Fires after the field has been marked as invalid.
16080              * @param {Roo.form.Field} this
16081              * @param {String} msg The validation message
16082              */
16083             invalid : true,
16084             /**
16085              * @event valid
16086              * Fires after the field has been validated with no errors.
16087              * @param {Roo.form.Field} this
16088              */
16089             valid : true,
16090              /**
16091              * @event keyup
16092              * Fires after the key up
16093              * @param {Roo.form.Field} this
16094              * @param {Roo.EventObject}  e The event Object
16095              */
16096             keyup : true
16097         });
16098     },
16099
16100     /**
16101      * Returns the name attribute of the field if available
16102      * @return {String} name The field name
16103      */
16104     getName: function(){
16105          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16106     },
16107
16108     // private
16109     onRender : function(ct, position){
16110         Roo.form.Field.superclass.onRender.call(this, ct, position);
16111         if(!this.el){
16112             var cfg = this.getAutoCreate();
16113             if(!cfg.name){
16114                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16115             }
16116             if (!cfg.name.length) {
16117                 delete cfg.name;
16118             }
16119             if(this.inputType){
16120                 cfg.type = this.inputType;
16121             }
16122             this.el = ct.createChild(cfg, position);
16123         }
16124         var type = this.el.dom.type;
16125         if(type){
16126             if(type == 'password'){
16127                 type = 'text';
16128             }
16129             this.el.addClass('x-form-'+type);
16130         }
16131         if(this.readOnly){
16132             this.el.dom.readOnly = true;
16133         }
16134         if(this.tabIndex !== undefined){
16135             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16136         }
16137
16138         this.el.addClass([this.fieldClass, this.cls]);
16139         this.initValue();
16140     },
16141
16142     /**
16143      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16144      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16145      * @return {Roo.form.Field} this
16146      */
16147     applyTo : function(target){
16148         this.allowDomMove = false;
16149         this.el = Roo.get(target);
16150         this.render(this.el.dom.parentNode);
16151         return this;
16152     },
16153
16154     // private
16155     initValue : function(){
16156         if(this.value !== undefined){
16157             this.setValue(this.value);
16158         }else if(this.el.dom.value.length > 0){
16159             this.setValue(this.el.dom.value);
16160         }
16161     },
16162
16163     /**
16164      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16165      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16166      */
16167     isDirty : function() {
16168         if(this.disabled) {
16169             return false;
16170         }
16171         return String(this.getValue()) !== String(this.originalValue);
16172     },
16173
16174     /**
16175      * stores the current value in loadedValue
16176      */
16177     resetHasChanged : function()
16178     {
16179         this.loadedValue = String(this.getValue());
16180     },
16181     /**
16182      * checks the current value against the 'loaded' value.
16183      * Note - will return false if 'resetHasChanged' has not been called first.
16184      */
16185     hasChanged : function()
16186     {
16187         if(this.disabled || this.readOnly) {
16188             return false;
16189         }
16190         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16191     },
16192     
16193     
16194     
16195     // private
16196     afterRender : function(){
16197         Roo.form.Field.superclass.afterRender.call(this);
16198         this.initEvents();
16199     },
16200
16201     // private
16202     fireKey : function(e){
16203         //Roo.log('field ' + e.getKey());
16204         if(e.isNavKeyPress()){
16205             this.fireEvent("specialkey", this, e);
16206         }
16207     },
16208
16209     /**
16210      * Resets the current field value to the originally loaded value and clears any validation messages
16211      */
16212     reset : function(){
16213         this.setValue(this.resetValue);
16214         this.originalValue = this.getValue();
16215         this.clearInvalid();
16216     },
16217
16218     // private
16219     initEvents : function(){
16220         // safari killled keypress - so keydown is now used..
16221         this.el.on("keydown" , this.fireKey,  this);
16222         this.el.on("focus", this.onFocus,  this);
16223         this.el.on("blur", this.onBlur,  this);
16224         this.el.relayEvent('keyup', this);
16225
16226         // reference to original value for reset
16227         this.originalValue = this.getValue();
16228         this.resetValue =  this.getValue();
16229     },
16230
16231     // private
16232     onFocus : function(){
16233         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16234             this.el.addClass(this.focusClass);
16235         }
16236         if(!this.hasFocus){
16237             this.hasFocus = true;
16238             this.startValue = this.getValue();
16239             this.fireEvent("focus", this);
16240         }
16241     },
16242
16243     beforeBlur : Roo.emptyFn,
16244
16245     // private
16246     onBlur : function(){
16247         this.beforeBlur();
16248         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16249             this.el.removeClass(this.focusClass);
16250         }
16251         this.hasFocus = false;
16252         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16253             this.validate();
16254         }
16255         var v = this.getValue();
16256         if(String(v) !== String(this.startValue)){
16257             this.fireEvent('change', this, v, this.startValue);
16258         }
16259         this.fireEvent("blur", this);
16260     },
16261
16262     /**
16263      * Returns whether or not the field value is currently valid
16264      * @param {Boolean} preventMark True to disable marking the field invalid
16265      * @return {Boolean} True if the value is valid, else false
16266      */
16267     isValid : function(preventMark){
16268         if(this.disabled){
16269             return true;
16270         }
16271         var restore = this.preventMark;
16272         this.preventMark = preventMark === true;
16273         var v = this.validateValue(this.processValue(this.getRawValue()));
16274         this.preventMark = restore;
16275         return v;
16276     },
16277
16278     /**
16279      * Validates the field value
16280      * @return {Boolean} True if the value is valid, else false
16281      */
16282     validate : function(){
16283         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16284             this.clearInvalid();
16285             return true;
16286         }
16287         return false;
16288     },
16289
16290     processValue : function(value){
16291         return value;
16292     },
16293
16294     // private
16295     // Subclasses should provide the validation implementation by overriding this
16296     validateValue : function(value){
16297         return true;
16298     },
16299
16300     /**
16301      * Mark this field as invalid
16302      * @param {String} msg The validation message
16303      */
16304     markInvalid : function(msg){
16305         if(!this.rendered || this.preventMark){ // not rendered
16306             return;
16307         }
16308         
16309         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16310         
16311         obj.el.addClass(this.invalidClass);
16312         msg = msg || this.invalidText;
16313         switch(this.msgTarget){
16314             case 'qtip':
16315                 obj.el.dom.qtip = msg;
16316                 obj.el.dom.qclass = 'x-form-invalid-tip';
16317                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16318                     Roo.QuickTips.enable();
16319                 }
16320                 break;
16321             case 'title':
16322                 this.el.dom.title = msg;
16323                 break;
16324             case 'under':
16325                 if(!this.errorEl){
16326                     var elp = this.el.findParent('.x-form-element', 5, true);
16327                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16328                     this.errorEl.setWidth(elp.getWidth(true)-20);
16329                 }
16330                 this.errorEl.update(msg);
16331                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16332                 break;
16333             case 'side':
16334                 if(!this.errorIcon){
16335                     var elp = this.el.findParent('.x-form-element', 5, true);
16336                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16337                 }
16338                 this.alignErrorIcon();
16339                 this.errorIcon.dom.qtip = msg;
16340                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16341                 this.errorIcon.show();
16342                 this.on('resize', this.alignErrorIcon, this);
16343                 break;
16344             default:
16345                 var t = Roo.getDom(this.msgTarget);
16346                 t.innerHTML = msg;
16347                 t.style.display = this.msgDisplay;
16348                 break;
16349         }
16350         this.fireEvent('invalid', this, msg);
16351     },
16352
16353     // private
16354     alignErrorIcon : function(){
16355         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16356     },
16357
16358     /**
16359      * Clear any invalid styles/messages for this field
16360      */
16361     clearInvalid : function(){
16362         if(!this.rendered || this.preventMark){ // not rendered
16363             return;
16364         }
16365         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16366         
16367         obj.el.removeClass(this.invalidClass);
16368         switch(this.msgTarget){
16369             case 'qtip':
16370                 obj.el.dom.qtip = '';
16371                 break;
16372             case 'title':
16373                 this.el.dom.title = '';
16374                 break;
16375             case 'under':
16376                 if(this.errorEl){
16377                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16378                 }
16379                 break;
16380             case 'side':
16381                 if(this.errorIcon){
16382                     this.errorIcon.dom.qtip = '';
16383                     this.errorIcon.hide();
16384                     this.un('resize', this.alignErrorIcon, this);
16385                 }
16386                 break;
16387             default:
16388                 var t = Roo.getDom(this.msgTarget);
16389                 t.innerHTML = '';
16390                 t.style.display = 'none';
16391                 break;
16392         }
16393         this.fireEvent('valid', this);
16394     },
16395
16396     /**
16397      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16398      * @return {Mixed} value The field value
16399      */
16400     getRawValue : function(){
16401         var v = this.el.getValue();
16402         
16403         return v;
16404     },
16405
16406     /**
16407      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16408      * @return {Mixed} value The field value
16409      */
16410     getValue : function(){
16411         var v = this.el.getValue();
16412          
16413         return v;
16414     },
16415
16416     /**
16417      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16418      * @param {Mixed} value The value to set
16419      */
16420     setRawValue : function(v){
16421         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16422     },
16423
16424     /**
16425      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16426      * @param {Mixed} value The value to set
16427      */
16428     setValue : function(v){
16429         this.value = v;
16430         if(this.rendered){
16431             this.el.dom.value = (v === null || v === undefined ? '' : v);
16432              this.validate();
16433         }
16434     },
16435
16436     adjustSize : function(w, h){
16437         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16438         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16439         return s;
16440     },
16441
16442     adjustWidth : function(tag, w){
16443         tag = tag.toLowerCase();
16444         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16445             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16446                 if(tag == 'input'){
16447                     return w + 2;
16448                 }
16449                 if(tag == 'textarea'){
16450                     return w-2;
16451                 }
16452             }else if(Roo.isOpera){
16453                 if(tag == 'input'){
16454                     return w + 2;
16455                 }
16456                 if(tag == 'textarea'){
16457                     return w-2;
16458                 }
16459             }
16460         }
16461         return w;
16462     }
16463 });
16464
16465
16466 // anything other than normal should be considered experimental
16467 Roo.form.Field.msgFx = {
16468     normal : {
16469         show: function(msgEl, f){
16470             msgEl.setDisplayed('block');
16471         },
16472
16473         hide : function(msgEl, f){
16474             msgEl.setDisplayed(false).update('');
16475         }
16476     },
16477
16478     slide : {
16479         show: function(msgEl, f){
16480             msgEl.slideIn('t', {stopFx:true});
16481         },
16482
16483         hide : function(msgEl, f){
16484             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16485         }
16486     },
16487
16488     slideRight : {
16489         show: function(msgEl, f){
16490             msgEl.fixDisplay();
16491             msgEl.alignTo(f.el, 'tl-tr');
16492             msgEl.slideIn('l', {stopFx:true});
16493         },
16494
16495         hide : function(msgEl, f){
16496             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16497         }
16498     }
16499 };/*
16500  * Based on:
16501  * Ext JS Library 1.1.1
16502  * Copyright(c) 2006-2007, Ext JS, LLC.
16503  *
16504  * Originally Released Under LGPL - original licence link has changed is not relivant.
16505  *
16506  * Fork - LGPL
16507  * <script type="text/javascript">
16508  */
16509  
16510
16511 /**
16512  * @class Roo.form.TextField
16513  * @extends Roo.form.Field
16514  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16515  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16516  * @constructor
16517  * Creates a new TextField
16518  * @param {Object} config Configuration options
16519  */
16520 Roo.form.TextField = function(config){
16521     Roo.form.TextField.superclass.constructor.call(this, config);
16522     this.addEvents({
16523         /**
16524          * @event autosize
16525          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16526          * according to the default logic, but this event provides a hook for the developer to apply additional
16527          * logic at runtime to resize the field if needed.
16528              * @param {Roo.form.Field} this This text field
16529              * @param {Number} width The new field width
16530              */
16531         autosize : true
16532     });
16533 };
16534
16535 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16536     /**
16537      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16538      */
16539     grow : false,
16540     /**
16541      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16542      */
16543     growMin : 30,
16544     /**
16545      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16546      */
16547     growMax : 800,
16548     /**
16549      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16550      */
16551     vtype : null,
16552     /**
16553      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16554      */
16555     maskRe : null,
16556     /**
16557      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16558      */
16559     disableKeyFilter : false,
16560     /**
16561      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16562      */
16563     allowBlank : true,
16564     /**
16565      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16566      */
16567     minLength : 0,
16568     /**
16569      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16570      */
16571     maxLength : Number.MAX_VALUE,
16572     /**
16573      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16574      */
16575     minLengthText : "The minimum length for this field is {0}",
16576     /**
16577      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16578      */
16579     maxLengthText : "The maximum length for this field is {0}",
16580     /**
16581      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16582      */
16583     selectOnFocus : false,
16584     /**
16585      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16586      */    
16587     allowLeadingSpace : false,
16588     /**
16589      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16590      */
16591     blankText : "This field is required",
16592     /**
16593      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16594      * If available, this function will be called only after the basic validators all return true, and will be passed the
16595      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16596      */
16597     validator : null,
16598     /**
16599      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16600      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16601      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16602      */
16603     regex : null,
16604     /**
16605      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16606      */
16607     regexText : "",
16608     /**
16609      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16610      */
16611     emptyText : null,
16612    
16613
16614     // private
16615     initEvents : function()
16616     {
16617         if (this.emptyText) {
16618             this.el.attr('placeholder', this.emptyText);
16619         }
16620         
16621         Roo.form.TextField.superclass.initEvents.call(this);
16622         if(this.validationEvent == 'keyup'){
16623             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16624             this.el.on('keyup', this.filterValidation, this);
16625         }
16626         else if(this.validationEvent !== false){
16627             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16628         }
16629         
16630         if(this.selectOnFocus){
16631             this.on("focus", this.preFocus, this);
16632         }
16633         if (!this.allowLeadingSpace) {
16634             this.on('blur', this.cleanLeadingSpace, this);
16635         }
16636         
16637         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16638             this.el.on("keypress", this.filterKeys, this);
16639         }
16640         if(this.grow){
16641             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16642             this.el.on("click", this.autoSize,  this);
16643         }
16644         if(this.el.is('input[type=password]') && Roo.isSafari){
16645             this.el.on('keydown', this.SafariOnKeyDown, this);
16646         }
16647     },
16648
16649     processValue : function(value){
16650         if(this.stripCharsRe){
16651             var newValue = value.replace(this.stripCharsRe, '');
16652             if(newValue !== value){
16653                 this.setRawValue(newValue);
16654                 return newValue;
16655             }
16656         }
16657         return value;
16658     },
16659
16660     filterValidation : function(e){
16661         if(!e.isNavKeyPress()){
16662             this.validationTask.delay(this.validationDelay);
16663         }
16664     },
16665
16666     // private
16667     onKeyUp : function(e){
16668         if(!e.isNavKeyPress()){
16669             this.autoSize();
16670         }
16671     },
16672     // private - clean the leading white space
16673     cleanLeadingSpace : function(e)
16674     {
16675         if ( this.inputType == 'file') {
16676             return;
16677         }
16678         
16679         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16680     },
16681     /**
16682      * Resets the current field value to the originally-loaded value and clears any validation messages.
16683      *  
16684      */
16685     reset : function(){
16686         Roo.form.TextField.superclass.reset.call(this);
16687        
16688     }, 
16689     // private
16690     preFocus : function(){
16691         
16692         if(this.selectOnFocus){
16693             this.el.dom.select();
16694         }
16695     },
16696
16697     
16698     // private
16699     filterKeys : function(e){
16700         var k = e.getKey();
16701         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16702             return;
16703         }
16704         var c = e.getCharCode(), cc = String.fromCharCode(c);
16705         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16706             return;
16707         }
16708         if(!this.maskRe.test(cc)){
16709             e.stopEvent();
16710         }
16711     },
16712
16713     setValue : function(v){
16714         
16715         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16716         
16717         this.autoSize();
16718     },
16719
16720     /**
16721      * Validates a value according to the field's validation rules and marks the field as invalid
16722      * if the validation fails
16723      * @param {Mixed} value The value to validate
16724      * @return {Boolean} True if the value is valid, else false
16725      */
16726     validateValue : function(value){
16727         if(value.length < 1)  { // if it's blank
16728              if(this.allowBlank){
16729                 this.clearInvalid();
16730                 return true;
16731              }else{
16732                 this.markInvalid(this.blankText);
16733                 return false;
16734              }
16735         }
16736         if(value.length < this.minLength){
16737             this.markInvalid(String.format(this.minLengthText, this.minLength));
16738             return false;
16739         }
16740         if(value.length > this.maxLength){
16741             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16742             return false;
16743         }
16744         if(this.vtype){
16745             var vt = Roo.form.VTypes;
16746             if(!vt[this.vtype](value, this)){
16747                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16748                 return false;
16749             }
16750         }
16751         if(typeof this.validator == "function"){
16752             var msg = this.validator(value);
16753             if(msg !== true){
16754                 this.markInvalid(msg);
16755                 return false;
16756             }
16757         }
16758         if(this.regex && !this.regex.test(value)){
16759             this.markInvalid(this.regexText);
16760             return false;
16761         }
16762         return true;
16763     },
16764
16765     /**
16766      * Selects text in this field
16767      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16768      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16769      */
16770     selectText : function(start, end){
16771         var v = this.getRawValue();
16772         if(v.length > 0){
16773             start = start === undefined ? 0 : start;
16774             end = end === undefined ? v.length : end;
16775             var d = this.el.dom;
16776             if(d.setSelectionRange){
16777                 d.setSelectionRange(start, end);
16778             }else if(d.createTextRange){
16779                 var range = d.createTextRange();
16780                 range.moveStart("character", start);
16781                 range.moveEnd("character", v.length-end);
16782                 range.select();
16783             }
16784         }
16785     },
16786
16787     /**
16788      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16789      * This only takes effect if grow = true, and fires the autosize event.
16790      */
16791     autoSize : function(){
16792         if(!this.grow || !this.rendered){
16793             return;
16794         }
16795         if(!this.metrics){
16796             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16797         }
16798         var el = this.el;
16799         var v = el.dom.value;
16800         var d = document.createElement('div');
16801         d.appendChild(document.createTextNode(v));
16802         v = d.innerHTML;
16803         d = null;
16804         v += "&#160;";
16805         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16806         this.el.setWidth(w);
16807         this.fireEvent("autosize", this, w);
16808     },
16809     
16810     // private
16811     SafariOnKeyDown : function(event)
16812     {
16813         // this is a workaround for a password hang bug on chrome/ webkit.
16814         
16815         var isSelectAll = false;
16816         
16817         if(this.el.dom.selectionEnd > 0){
16818             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16819         }
16820         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16821             event.preventDefault();
16822             this.setValue('');
16823             return;
16824         }
16825         
16826         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16827             
16828             event.preventDefault();
16829             // this is very hacky as keydown always get's upper case.
16830             
16831             var cc = String.fromCharCode(event.getCharCode());
16832             
16833             
16834             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16835             
16836         }
16837         
16838         
16839     }
16840 });/*
16841  * Based on:
16842  * Ext JS Library 1.1.1
16843  * Copyright(c) 2006-2007, Ext JS, LLC.
16844  *
16845  * Originally Released Under LGPL - original licence link has changed is not relivant.
16846  *
16847  * Fork - LGPL
16848  * <script type="text/javascript">
16849  */
16850  
16851 /**
16852  * @class Roo.form.Hidden
16853  * @extends Roo.form.TextField
16854  * Simple Hidden element used on forms 
16855  * 
16856  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16857  * 
16858  * @constructor
16859  * Creates a new Hidden form element.
16860  * @param {Object} config Configuration options
16861  */
16862
16863
16864
16865 // easy hidden field...
16866 Roo.form.Hidden = function(config){
16867     Roo.form.Hidden.superclass.constructor.call(this, config);
16868 };
16869   
16870 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16871     fieldLabel:      '',
16872     inputType:      'hidden',
16873     width:          50,
16874     allowBlank:     true,
16875     labelSeparator: '',
16876     hidden:         true,
16877     itemCls :       'x-form-item-display-none'
16878
16879
16880 });
16881
16882
16883 /*
16884  * Based on:
16885  * Ext JS Library 1.1.1
16886  * Copyright(c) 2006-2007, Ext JS, LLC.
16887  *
16888  * Originally Released Under LGPL - original licence link has changed is not relivant.
16889  *
16890  * Fork - LGPL
16891  * <script type="text/javascript">
16892  */
16893  
16894 /**
16895  * @class Roo.form.TriggerField
16896  * @extends Roo.form.TextField
16897  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16898  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16899  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16900  * for which you can provide a custom implementation.  For example:
16901  * <pre><code>
16902 var trigger = new Roo.form.TriggerField();
16903 trigger.onTriggerClick = myTriggerFn;
16904 trigger.applyTo('my-field');
16905 </code></pre>
16906  *
16907  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16908  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16909  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16910  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16911  * @constructor
16912  * Create a new TriggerField.
16913  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16914  * to the base TextField)
16915  */
16916 Roo.form.TriggerField = function(config){
16917     this.mimicing = false;
16918     Roo.form.TriggerField.superclass.constructor.call(this, config);
16919 };
16920
16921 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16922     /**
16923      * @cfg {String} triggerClass A CSS class to apply to the trigger
16924      */
16925     /**
16926      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16927      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16928      */
16929     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16930     /**
16931      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16932      */
16933     hideTrigger:false,
16934
16935     /** @cfg {Boolean} grow @hide */
16936     /** @cfg {Number} growMin @hide */
16937     /** @cfg {Number} growMax @hide */
16938
16939     /**
16940      * @hide 
16941      * @method
16942      */
16943     autoSize: Roo.emptyFn,
16944     // private
16945     monitorTab : true,
16946     // private
16947     deferHeight : true,
16948
16949     
16950     actionMode : 'wrap',
16951     // private
16952     onResize : function(w, h){
16953         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16954         if(typeof w == 'number'){
16955             var x = w - this.trigger.getWidth();
16956             this.el.setWidth(this.adjustWidth('input', x));
16957             this.trigger.setStyle('left', x+'px');
16958         }
16959     },
16960
16961     // private
16962     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16963
16964     // private
16965     getResizeEl : function(){
16966         return this.wrap;
16967     },
16968
16969     // private
16970     getPositionEl : function(){
16971         return this.wrap;
16972     },
16973
16974     // private
16975     alignErrorIcon : function(){
16976         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
16977     },
16978
16979     // private
16980     onRender : function(ct, position){
16981         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
16982         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
16983         this.trigger = this.wrap.createChild(this.triggerConfig ||
16984                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
16985         if(this.hideTrigger){
16986             this.trigger.setDisplayed(false);
16987         }
16988         this.initTrigger();
16989         if(!this.width){
16990             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
16991         }
16992     },
16993
16994     // private
16995     initTrigger : function(){
16996         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
16997         this.trigger.addClassOnOver('x-form-trigger-over');
16998         this.trigger.addClassOnClick('x-form-trigger-click');
16999     },
17000
17001     // private
17002     onDestroy : function(){
17003         if(this.trigger){
17004             this.trigger.removeAllListeners();
17005             this.trigger.remove();
17006         }
17007         if(this.wrap){
17008             this.wrap.remove();
17009         }
17010         Roo.form.TriggerField.superclass.onDestroy.call(this);
17011     },
17012
17013     // private
17014     onFocus : function(){
17015         Roo.form.TriggerField.superclass.onFocus.call(this);
17016         if(!this.mimicing){
17017             this.wrap.addClass('x-trigger-wrap-focus');
17018             this.mimicing = true;
17019             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17020             if(this.monitorTab){
17021                 this.el.on("keydown", this.checkTab, this);
17022             }
17023         }
17024     },
17025
17026     // private
17027     checkTab : function(e){
17028         if(e.getKey() == e.TAB){
17029             this.triggerBlur();
17030         }
17031     },
17032
17033     // private
17034     onBlur : function(){
17035         // do nothing
17036     },
17037
17038     // private
17039     mimicBlur : function(e, t){
17040         if(!this.wrap.contains(t) && this.validateBlur()){
17041             this.triggerBlur();
17042         }
17043     },
17044
17045     // private
17046     triggerBlur : function(){
17047         this.mimicing = false;
17048         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17049         if(this.monitorTab){
17050             this.el.un("keydown", this.checkTab, this);
17051         }
17052         this.wrap.removeClass('x-trigger-wrap-focus');
17053         Roo.form.TriggerField.superclass.onBlur.call(this);
17054     },
17055
17056     // private
17057     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17058     validateBlur : function(e, t){
17059         return true;
17060     },
17061
17062     // private
17063     onDisable : function(){
17064         Roo.form.TriggerField.superclass.onDisable.call(this);
17065         if(this.wrap){
17066             this.wrap.addClass('x-item-disabled');
17067         }
17068     },
17069
17070     // private
17071     onEnable : function(){
17072         Roo.form.TriggerField.superclass.onEnable.call(this);
17073         if(this.wrap){
17074             this.wrap.removeClass('x-item-disabled');
17075         }
17076     },
17077
17078     // private
17079     onShow : function(){
17080         var ae = this.getActionEl();
17081         
17082         if(ae){
17083             ae.dom.style.display = '';
17084             ae.dom.style.visibility = 'visible';
17085         }
17086     },
17087
17088     // private
17089     
17090     onHide : function(){
17091         var ae = this.getActionEl();
17092         ae.dom.style.display = 'none';
17093     },
17094
17095     /**
17096      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17097      * by an implementing function.
17098      * @method
17099      * @param {EventObject} e
17100      */
17101     onTriggerClick : Roo.emptyFn
17102 });
17103
17104 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17105 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17106 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17107 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17108     initComponent : function(){
17109         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17110
17111         this.triggerConfig = {
17112             tag:'span', cls:'x-form-twin-triggers', cn:[
17113             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17114             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17115         ]};
17116     },
17117
17118     getTrigger : function(index){
17119         return this.triggers[index];
17120     },
17121
17122     initTrigger : function(){
17123         var ts = this.trigger.select('.x-form-trigger', true);
17124         this.wrap.setStyle('overflow', 'hidden');
17125         var triggerField = this;
17126         ts.each(function(t, all, index){
17127             t.hide = function(){
17128                 var w = triggerField.wrap.getWidth();
17129                 this.dom.style.display = 'none';
17130                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17131             };
17132             t.show = function(){
17133                 var w = triggerField.wrap.getWidth();
17134                 this.dom.style.display = '';
17135                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17136             };
17137             var triggerIndex = 'Trigger'+(index+1);
17138
17139             if(this['hide'+triggerIndex]){
17140                 t.dom.style.display = 'none';
17141             }
17142             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17143             t.addClassOnOver('x-form-trigger-over');
17144             t.addClassOnClick('x-form-trigger-click');
17145         }, this);
17146         this.triggers = ts.elements;
17147     },
17148
17149     onTrigger1Click : Roo.emptyFn,
17150     onTrigger2Click : Roo.emptyFn
17151 });/*
17152  * Based on:
17153  * Ext JS Library 1.1.1
17154  * Copyright(c) 2006-2007, Ext JS, LLC.
17155  *
17156  * Originally Released Under LGPL - original licence link has changed is not relivant.
17157  *
17158  * Fork - LGPL
17159  * <script type="text/javascript">
17160  */
17161  
17162 /**
17163  * @class Roo.form.TextArea
17164  * @extends Roo.form.TextField
17165  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17166  * support for auto-sizing.
17167  * @constructor
17168  * Creates a new TextArea
17169  * @param {Object} config Configuration options
17170  */
17171 Roo.form.TextArea = function(config){
17172     Roo.form.TextArea.superclass.constructor.call(this, config);
17173     // these are provided exchanges for backwards compat
17174     // minHeight/maxHeight were replaced by growMin/growMax to be
17175     // compatible with TextField growing config values
17176     if(this.minHeight !== undefined){
17177         this.growMin = this.minHeight;
17178     }
17179     if(this.maxHeight !== undefined){
17180         this.growMax = this.maxHeight;
17181     }
17182 };
17183
17184 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17185     /**
17186      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17187      */
17188     growMin : 60,
17189     /**
17190      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17191      */
17192     growMax: 1000,
17193     /**
17194      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17195      * in the field (equivalent to setting overflow: hidden, defaults to false)
17196      */
17197     preventScrollbars: false,
17198     /**
17199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17200      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17201      */
17202
17203     // private
17204     onRender : function(ct, position){
17205         if(!this.el){
17206             this.defaultAutoCreate = {
17207                 tag: "textarea",
17208                 style:"width:300px;height:60px;",
17209                 autocomplete: "new-password"
17210             };
17211         }
17212         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17213         if(this.grow){
17214             this.textSizeEl = Roo.DomHelper.append(document.body, {
17215                 tag: "pre", cls: "x-form-grow-sizer"
17216             });
17217             if(this.preventScrollbars){
17218                 this.el.setStyle("overflow", "hidden");
17219             }
17220             this.el.setHeight(this.growMin);
17221         }
17222     },
17223
17224     onDestroy : function(){
17225         if(this.textSizeEl){
17226             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17227         }
17228         Roo.form.TextArea.superclass.onDestroy.call(this);
17229     },
17230
17231     // private
17232     onKeyUp : function(e){
17233         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17234             this.autoSize();
17235         }
17236     },
17237
17238     /**
17239      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17240      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17241      */
17242     autoSize : function(){
17243         if(!this.grow || !this.textSizeEl){
17244             return;
17245         }
17246         var el = this.el;
17247         var v = el.dom.value;
17248         var ts = this.textSizeEl;
17249
17250         ts.innerHTML = '';
17251         ts.appendChild(document.createTextNode(v));
17252         v = ts.innerHTML;
17253
17254         Roo.fly(ts).setWidth(this.el.getWidth());
17255         if(v.length < 1){
17256             v = "&#160;&#160;";
17257         }else{
17258             if(Roo.isIE){
17259                 v = v.replace(/\n/g, '<p>&#160;</p>');
17260             }
17261             v += "&#160;\n&#160;";
17262         }
17263         ts.innerHTML = v;
17264         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17265         if(h != this.lastHeight){
17266             this.lastHeight = h;
17267             this.el.setHeight(h);
17268             this.fireEvent("autosize", this, h);
17269         }
17270     }
17271 });/*
17272  * Based on:
17273  * Ext JS Library 1.1.1
17274  * Copyright(c) 2006-2007, Ext JS, LLC.
17275  *
17276  * Originally Released Under LGPL - original licence link has changed is not relivant.
17277  *
17278  * Fork - LGPL
17279  * <script type="text/javascript">
17280  */
17281  
17282
17283 /**
17284  * @class Roo.form.NumberField
17285  * @extends Roo.form.TextField
17286  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17287  * @constructor
17288  * Creates a new NumberField
17289  * @param {Object} config Configuration options
17290  */
17291 Roo.form.NumberField = function(config){
17292     Roo.form.NumberField.superclass.constructor.call(this, config);
17293 };
17294
17295 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17296     /**
17297      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17298      */
17299     fieldClass: "x-form-field x-form-num-field",
17300     /**
17301      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17302      */
17303     allowDecimals : true,
17304     /**
17305      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17306      */
17307     decimalSeparator : ".",
17308     /**
17309      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17310      */
17311     decimalPrecision : 2,
17312     /**
17313      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17314      */
17315     allowNegative : true,
17316     /**
17317      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17318      */
17319     minValue : Number.NEGATIVE_INFINITY,
17320     /**
17321      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17322      */
17323     maxValue : Number.MAX_VALUE,
17324     /**
17325      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17326      */
17327     minText : "The minimum value for this field is {0}",
17328     /**
17329      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17330      */
17331     maxText : "The maximum value for this field is {0}",
17332     /**
17333      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17334      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17335      */
17336     nanText : "{0} is not a valid number",
17337
17338     // private
17339     initEvents : function(){
17340         Roo.form.NumberField.superclass.initEvents.call(this);
17341         var allowed = "0123456789";
17342         if(this.allowDecimals){
17343             allowed += this.decimalSeparator;
17344         }
17345         if(this.allowNegative){
17346             allowed += "-";
17347         }
17348         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17349         var keyPress = function(e){
17350             var k = e.getKey();
17351             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17352                 return;
17353             }
17354             var c = e.getCharCode();
17355             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17356                 e.stopEvent();
17357             }
17358         };
17359         this.el.on("keypress", keyPress, this);
17360     },
17361
17362     // private
17363     validateValue : function(value){
17364         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17365             return false;
17366         }
17367         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17368              return true;
17369         }
17370         var num = this.parseValue(value);
17371         if(isNaN(num)){
17372             this.markInvalid(String.format(this.nanText, value));
17373             return false;
17374         }
17375         if(num < this.minValue){
17376             this.markInvalid(String.format(this.minText, this.minValue));
17377             return false;
17378         }
17379         if(num > this.maxValue){
17380             this.markInvalid(String.format(this.maxText, this.maxValue));
17381             return false;
17382         }
17383         return true;
17384     },
17385
17386     getValue : function(){
17387         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17388     },
17389
17390     // private
17391     parseValue : function(value){
17392         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17393         return isNaN(value) ? '' : value;
17394     },
17395
17396     // private
17397     fixPrecision : function(value){
17398         var nan = isNaN(value);
17399         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17400             return nan ? '' : value;
17401         }
17402         return parseFloat(value).toFixed(this.decimalPrecision);
17403     },
17404
17405     setValue : function(v){
17406         v = this.fixPrecision(v);
17407         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17408     },
17409
17410     // private
17411     decimalPrecisionFcn : function(v){
17412         return Math.floor(v);
17413     },
17414
17415     beforeBlur : function(){
17416         var v = this.parseValue(this.getRawValue());
17417         if(v){
17418             this.setValue(v);
17419         }
17420     }
17421 });/*
17422  * Based on:
17423  * Ext JS Library 1.1.1
17424  * Copyright(c) 2006-2007, Ext JS, LLC.
17425  *
17426  * Originally Released Under LGPL - original licence link has changed is not relivant.
17427  *
17428  * Fork - LGPL
17429  * <script type="text/javascript">
17430  */
17431  
17432 /**
17433  * @class Roo.form.DateField
17434  * @extends Roo.form.TriggerField
17435  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17436 * @constructor
17437 * Create a new DateField
17438 * @param {Object} config
17439  */
17440 Roo.form.DateField = function(config)
17441 {
17442     Roo.form.DateField.superclass.constructor.call(this, config);
17443     
17444       this.addEvents({
17445          
17446         /**
17447          * @event select
17448          * Fires when a date is selected
17449              * @param {Roo.form.DateField} combo This combo box
17450              * @param {Date} date The date selected
17451              */
17452         'select' : true
17453          
17454     });
17455     
17456     
17457     if(typeof this.minValue == "string") {
17458         this.minValue = this.parseDate(this.minValue);
17459     }
17460     if(typeof this.maxValue == "string") {
17461         this.maxValue = this.parseDate(this.maxValue);
17462     }
17463     this.ddMatch = null;
17464     if(this.disabledDates){
17465         var dd = this.disabledDates;
17466         var re = "(?:";
17467         for(var i = 0; i < dd.length; i++){
17468             re += dd[i];
17469             if(i != dd.length-1) {
17470                 re += "|";
17471             }
17472         }
17473         this.ddMatch = new RegExp(re + ")");
17474     }
17475 };
17476
17477 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17478     /**
17479      * @cfg {String} format
17480      * The default date format string which can be overriden for localization support.  The format must be
17481      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17482      */
17483     format : "m/d/y",
17484     /**
17485      * @cfg {String} altFormats
17486      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17487      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17488      */
17489     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17490     /**
17491      * @cfg {Array} disabledDays
17492      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17493      */
17494     disabledDays : null,
17495     /**
17496      * @cfg {String} disabledDaysText
17497      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17498      */
17499     disabledDaysText : "Disabled",
17500     /**
17501      * @cfg {Array} disabledDates
17502      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17503      * expression so they are very powerful. Some examples:
17504      * <ul>
17505      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17506      * <li>["03/08", "09/16"] would disable those days for every year</li>
17507      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17508      * <li>["03/../2006"] would disable every day in March 2006</li>
17509      * <li>["^03"] would disable every day in every March</li>
17510      * </ul>
17511      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17512      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17513      */
17514     disabledDates : null,
17515     /**
17516      * @cfg {String} disabledDatesText
17517      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17518      */
17519     disabledDatesText : "Disabled",
17520     /**
17521      * @cfg {Date/String} minValue
17522      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17523      * valid format (defaults to null).
17524      */
17525     minValue : null,
17526     /**
17527      * @cfg {Date/String} maxValue
17528      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17529      * valid format (defaults to null).
17530      */
17531     maxValue : null,
17532     /**
17533      * @cfg {String} minText
17534      * The error text to display when the date in the cell is before minValue (defaults to
17535      * 'The date in this field must be after {minValue}').
17536      */
17537     minText : "The date in this field must be equal to or after {0}",
17538     /**
17539      * @cfg {String} maxText
17540      * The error text to display when the date in the cell is after maxValue (defaults to
17541      * 'The date in this field must be before {maxValue}').
17542      */
17543     maxText : "The date in this field must be equal to or before {0}",
17544     /**
17545      * @cfg {String} invalidText
17546      * The error text to display when the date in the field is invalid (defaults to
17547      * '{value} is not a valid date - it must be in the format {format}').
17548      */
17549     invalidText : "{0} is not a valid date - it must be in the format {1}",
17550     /**
17551      * @cfg {String} triggerClass
17552      * An additional CSS class used to style the trigger button.  The trigger will always get the
17553      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17554      * which displays a calendar icon).
17555      */
17556     triggerClass : 'x-form-date-trigger',
17557     
17558
17559     /**
17560      * @cfg {Boolean} useIso
17561      * if enabled, then the date field will use a hidden field to store the 
17562      * real value as iso formated date. default (false)
17563      */ 
17564     useIso : false,
17565     /**
17566      * @cfg {String/Object} autoCreate
17567      * A DomHelper element spec, or true for a default element spec (defaults to
17568      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17569      */ 
17570     // private
17571     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17572     
17573     // private
17574     hiddenField: false,
17575     
17576     onRender : function(ct, position)
17577     {
17578         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17579         if (this.useIso) {
17580             //this.el.dom.removeAttribute('name'); 
17581             Roo.log("Changing name?");
17582             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17583             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17584                     'before', true);
17585             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17586             // prevent input submission
17587             this.hiddenName = this.name;
17588         }
17589             
17590             
17591     },
17592     
17593     // private
17594     validateValue : function(value)
17595     {
17596         value = this.formatDate(value);
17597         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17598             Roo.log('super failed');
17599             return false;
17600         }
17601         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17602              return true;
17603         }
17604         var svalue = value;
17605         value = this.parseDate(value);
17606         if(!value){
17607             Roo.log('parse date failed' + svalue);
17608             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17609             return false;
17610         }
17611         var time = value.getTime();
17612         if(this.minValue && time < this.minValue.getTime()){
17613             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17614             return false;
17615         }
17616         if(this.maxValue && time > this.maxValue.getTime()){
17617             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17618             return false;
17619         }
17620         if(this.disabledDays){
17621             var day = value.getDay();
17622             for(var i = 0; i < this.disabledDays.length; i++) {
17623                 if(day === this.disabledDays[i]){
17624                     this.markInvalid(this.disabledDaysText);
17625                     return false;
17626                 }
17627             }
17628         }
17629         var fvalue = this.formatDate(value);
17630         if(this.ddMatch && this.ddMatch.test(fvalue)){
17631             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17632             return false;
17633         }
17634         return true;
17635     },
17636
17637     // private
17638     // Provides logic to override the default TriggerField.validateBlur which just returns true
17639     validateBlur : function(){
17640         return !this.menu || !this.menu.isVisible();
17641     },
17642     
17643     getName: function()
17644     {
17645         // returns hidden if it's set..
17646         if (!this.rendered) {return ''};
17647         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17648         
17649     },
17650
17651     /**
17652      * Returns the current date value of the date field.
17653      * @return {Date} The date value
17654      */
17655     getValue : function(){
17656         
17657         return  this.hiddenField ?
17658                 this.hiddenField.value :
17659                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17660     },
17661
17662     /**
17663      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17664      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17665      * (the default format used is "m/d/y").
17666      * <br />Usage:
17667      * <pre><code>
17668 //All of these calls set the same date value (May 4, 2006)
17669
17670 //Pass a date object:
17671 var dt = new Date('5/4/06');
17672 dateField.setValue(dt);
17673
17674 //Pass a date string (default format):
17675 dateField.setValue('5/4/06');
17676
17677 //Pass a date string (custom format):
17678 dateField.format = 'Y-m-d';
17679 dateField.setValue('2006-5-4');
17680 </code></pre>
17681      * @param {String/Date} date The date or valid date string
17682      */
17683     setValue : function(date){
17684         if (this.hiddenField) {
17685             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17686         }
17687         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17688         // make sure the value field is always stored as a date..
17689         this.value = this.parseDate(date);
17690         
17691         
17692     },
17693
17694     // private
17695     parseDate : function(value){
17696         if(!value || value instanceof Date){
17697             return value;
17698         }
17699         var v = Date.parseDate(value, this.format);
17700          if (!v && this.useIso) {
17701             v = Date.parseDate(value, 'Y-m-d');
17702         }
17703         if(!v && this.altFormats){
17704             if(!this.altFormatsArray){
17705                 this.altFormatsArray = this.altFormats.split("|");
17706             }
17707             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17708                 v = Date.parseDate(value, this.altFormatsArray[i]);
17709             }
17710         }
17711         return v;
17712     },
17713
17714     // private
17715     formatDate : function(date, fmt){
17716         return (!date || !(date instanceof Date)) ?
17717                date : date.dateFormat(fmt || this.format);
17718     },
17719
17720     // private
17721     menuListeners : {
17722         select: function(m, d){
17723             
17724             this.setValue(d);
17725             this.fireEvent('select', this, d);
17726         },
17727         show : function(){ // retain focus styling
17728             this.onFocus();
17729         },
17730         hide : function(){
17731             this.focus.defer(10, this);
17732             var ml = this.menuListeners;
17733             this.menu.un("select", ml.select,  this);
17734             this.menu.un("show", ml.show,  this);
17735             this.menu.un("hide", ml.hide,  this);
17736         }
17737     },
17738
17739     // private
17740     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17741     onTriggerClick : function(){
17742         if(this.disabled){
17743             return;
17744         }
17745         if(this.menu == null){
17746             this.menu = new Roo.menu.DateMenu();
17747         }
17748         Roo.apply(this.menu.picker,  {
17749             showClear: this.allowBlank,
17750             minDate : this.minValue,
17751             maxDate : this.maxValue,
17752             disabledDatesRE : this.ddMatch,
17753             disabledDatesText : this.disabledDatesText,
17754             disabledDays : this.disabledDays,
17755             disabledDaysText : this.disabledDaysText,
17756             format : this.useIso ? 'Y-m-d' : this.format,
17757             minText : String.format(this.minText, this.formatDate(this.minValue)),
17758             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17759         });
17760         this.menu.on(Roo.apply({}, this.menuListeners, {
17761             scope:this
17762         }));
17763         this.menu.picker.setValue(this.getValue() || new Date());
17764         this.menu.show(this.el, "tl-bl?");
17765     },
17766
17767     beforeBlur : function(){
17768         var v = this.parseDate(this.getRawValue());
17769         if(v){
17770             this.setValue(v);
17771         }
17772     },
17773
17774     /*@
17775      * overide
17776      * 
17777      */
17778     isDirty : function() {
17779         if(this.disabled) {
17780             return false;
17781         }
17782         
17783         if(typeof(this.startValue) === 'undefined'){
17784             return false;
17785         }
17786         
17787         return String(this.getValue()) !== String(this.startValue);
17788         
17789     },
17790     // @overide
17791     cleanLeadingSpace : function(e)
17792     {
17793        return;
17794     }
17795     
17796 });/*
17797  * Based on:
17798  * Ext JS Library 1.1.1
17799  * Copyright(c) 2006-2007, Ext JS, LLC.
17800  *
17801  * Originally Released Under LGPL - original licence link has changed is not relivant.
17802  *
17803  * Fork - LGPL
17804  * <script type="text/javascript">
17805  */
17806  
17807 /**
17808  * @class Roo.form.MonthField
17809  * @extends Roo.form.TriggerField
17810  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17811 * @constructor
17812 * Create a new MonthField
17813 * @param {Object} config
17814  */
17815 Roo.form.MonthField = function(config){
17816     
17817     Roo.form.MonthField.superclass.constructor.call(this, config);
17818     
17819       this.addEvents({
17820          
17821         /**
17822          * @event select
17823          * Fires when a date is selected
17824              * @param {Roo.form.MonthFieeld} combo This combo box
17825              * @param {Date} date The date selected
17826              */
17827         'select' : true
17828          
17829     });
17830     
17831     
17832     if(typeof this.minValue == "string") {
17833         this.minValue = this.parseDate(this.minValue);
17834     }
17835     if(typeof this.maxValue == "string") {
17836         this.maxValue = this.parseDate(this.maxValue);
17837     }
17838     this.ddMatch = null;
17839     if(this.disabledDates){
17840         var dd = this.disabledDates;
17841         var re = "(?:";
17842         for(var i = 0; i < dd.length; i++){
17843             re += dd[i];
17844             if(i != dd.length-1) {
17845                 re += "|";
17846             }
17847         }
17848         this.ddMatch = new RegExp(re + ")");
17849     }
17850 };
17851
17852 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17853     /**
17854      * @cfg {String} format
17855      * The default date format string which can be overriden for localization support.  The format must be
17856      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17857      */
17858     format : "M Y",
17859     /**
17860      * @cfg {String} altFormats
17861      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17862      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17863      */
17864     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17865     /**
17866      * @cfg {Array} disabledDays
17867      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17868      */
17869     disabledDays : [0,1,2,3,4,5,6],
17870     /**
17871      * @cfg {String} disabledDaysText
17872      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17873      */
17874     disabledDaysText : "Disabled",
17875     /**
17876      * @cfg {Array} disabledDates
17877      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17878      * expression so they are very powerful. Some examples:
17879      * <ul>
17880      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17881      * <li>["03/08", "09/16"] would disable those days for every year</li>
17882      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17883      * <li>["03/../2006"] would disable every day in March 2006</li>
17884      * <li>["^03"] would disable every day in every March</li>
17885      * </ul>
17886      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17887      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17888      */
17889     disabledDates : null,
17890     /**
17891      * @cfg {String} disabledDatesText
17892      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17893      */
17894     disabledDatesText : "Disabled",
17895     /**
17896      * @cfg {Date/String} minValue
17897      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17898      * valid format (defaults to null).
17899      */
17900     minValue : null,
17901     /**
17902      * @cfg {Date/String} maxValue
17903      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17904      * valid format (defaults to null).
17905      */
17906     maxValue : null,
17907     /**
17908      * @cfg {String} minText
17909      * The error text to display when the date in the cell is before minValue (defaults to
17910      * 'The date in this field must be after {minValue}').
17911      */
17912     minText : "The date in this field must be equal to or after {0}",
17913     /**
17914      * @cfg {String} maxTextf
17915      * The error text to display when the date in the cell is after maxValue (defaults to
17916      * 'The date in this field must be before {maxValue}').
17917      */
17918     maxText : "The date in this field must be equal to or before {0}",
17919     /**
17920      * @cfg {String} invalidText
17921      * The error text to display when the date in the field is invalid (defaults to
17922      * '{value} is not a valid date - it must be in the format {format}').
17923      */
17924     invalidText : "{0} is not a valid date - it must be in the format {1}",
17925     /**
17926      * @cfg {String} triggerClass
17927      * An additional CSS class used to style the trigger button.  The trigger will always get the
17928      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17929      * which displays a calendar icon).
17930      */
17931     triggerClass : 'x-form-date-trigger',
17932     
17933
17934     /**
17935      * @cfg {Boolean} useIso
17936      * if enabled, then the date field will use a hidden field to store the 
17937      * real value as iso formated date. default (true)
17938      */ 
17939     useIso : true,
17940     /**
17941      * @cfg {String/Object} autoCreate
17942      * A DomHelper element spec, or true for a default element spec (defaults to
17943      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17944      */ 
17945     // private
17946     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17947     
17948     // private
17949     hiddenField: false,
17950     
17951     hideMonthPicker : false,
17952     
17953     onRender : function(ct, position)
17954     {
17955         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17956         if (this.useIso) {
17957             this.el.dom.removeAttribute('name'); 
17958             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17959                     'before', true);
17960             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17961             // prevent input submission
17962             this.hiddenName = this.name;
17963         }
17964             
17965             
17966     },
17967     
17968     // private
17969     validateValue : function(value)
17970     {
17971         value = this.formatDate(value);
17972         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
17973             return false;
17974         }
17975         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17976              return true;
17977         }
17978         var svalue = value;
17979         value = this.parseDate(value);
17980         if(!value){
17981             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17982             return false;
17983         }
17984         var time = value.getTime();
17985         if(this.minValue && time < this.minValue.getTime()){
17986             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17987             return false;
17988         }
17989         if(this.maxValue && time > this.maxValue.getTime()){
17990             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17991             return false;
17992         }
17993         /*if(this.disabledDays){
17994             var day = value.getDay();
17995             for(var i = 0; i < this.disabledDays.length; i++) {
17996                 if(day === this.disabledDays[i]){
17997                     this.markInvalid(this.disabledDaysText);
17998                     return false;
17999                 }
18000             }
18001         }
18002         */
18003         var fvalue = this.formatDate(value);
18004         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18005             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18006             return false;
18007         }
18008         */
18009         return true;
18010     },
18011
18012     // private
18013     // Provides logic to override the default TriggerField.validateBlur which just returns true
18014     validateBlur : function(){
18015         return !this.menu || !this.menu.isVisible();
18016     },
18017
18018     /**
18019      * Returns the current date value of the date field.
18020      * @return {Date} The date value
18021      */
18022     getValue : function(){
18023         
18024         
18025         
18026         return  this.hiddenField ?
18027                 this.hiddenField.value :
18028                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18029     },
18030
18031     /**
18032      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18033      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18034      * (the default format used is "m/d/y").
18035      * <br />Usage:
18036      * <pre><code>
18037 //All of these calls set the same date value (May 4, 2006)
18038
18039 //Pass a date object:
18040 var dt = new Date('5/4/06');
18041 monthField.setValue(dt);
18042
18043 //Pass a date string (default format):
18044 monthField.setValue('5/4/06');
18045
18046 //Pass a date string (custom format):
18047 monthField.format = 'Y-m-d';
18048 monthField.setValue('2006-5-4');
18049 </code></pre>
18050      * @param {String/Date} date The date or valid date string
18051      */
18052     setValue : function(date){
18053         Roo.log('month setValue' + date);
18054         // can only be first of month..
18055         
18056         var val = this.parseDate(date);
18057         
18058         if (this.hiddenField) {
18059             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18060         }
18061         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18062         this.value = this.parseDate(date);
18063     },
18064
18065     // private
18066     parseDate : function(value){
18067         if(!value || value instanceof Date){
18068             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18069             return value;
18070         }
18071         var v = Date.parseDate(value, this.format);
18072         if (!v && this.useIso) {
18073             v = Date.parseDate(value, 'Y-m-d');
18074         }
18075         if (v) {
18076             // 
18077             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18078         }
18079         
18080         
18081         if(!v && this.altFormats){
18082             if(!this.altFormatsArray){
18083                 this.altFormatsArray = this.altFormats.split("|");
18084             }
18085             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18086                 v = Date.parseDate(value, this.altFormatsArray[i]);
18087             }
18088         }
18089         return v;
18090     },
18091
18092     // private
18093     formatDate : function(date, fmt){
18094         return (!date || !(date instanceof Date)) ?
18095                date : date.dateFormat(fmt || this.format);
18096     },
18097
18098     // private
18099     menuListeners : {
18100         select: function(m, d){
18101             this.setValue(d);
18102             this.fireEvent('select', this, d);
18103         },
18104         show : function(){ // retain focus styling
18105             this.onFocus();
18106         },
18107         hide : function(){
18108             this.focus.defer(10, this);
18109             var ml = this.menuListeners;
18110             this.menu.un("select", ml.select,  this);
18111             this.menu.un("show", ml.show,  this);
18112             this.menu.un("hide", ml.hide,  this);
18113         }
18114     },
18115     // private
18116     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18117     onTriggerClick : function(){
18118         if(this.disabled){
18119             return;
18120         }
18121         if(this.menu == null){
18122             this.menu = new Roo.menu.DateMenu();
18123            
18124         }
18125         
18126         Roo.apply(this.menu.picker,  {
18127             
18128             showClear: this.allowBlank,
18129             minDate : this.minValue,
18130             maxDate : this.maxValue,
18131             disabledDatesRE : this.ddMatch,
18132             disabledDatesText : this.disabledDatesText,
18133             
18134             format : this.useIso ? 'Y-m-d' : this.format,
18135             minText : String.format(this.minText, this.formatDate(this.minValue)),
18136             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18137             
18138         });
18139          this.menu.on(Roo.apply({}, this.menuListeners, {
18140             scope:this
18141         }));
18142        
18143         
18144         var m = this.menu;
18145         var p = m.picker;
18146         
18147         // hide month picker get's called when we called by 'before hide';
18148         
18149         var ignorehide = true;
18150         p.hideMonthPicker  = function(disableAnim){
18151             if (ignorehide) {
18152                 return;
18153             }
18154              if(this.monthPicker){
18155                 Roo.log("hideMonthPicker called");
18156                 if(disableAnim === true){
18157                     this.monthPicker.hide();
18158                 }else{
18159                     this.monthPicker.slideOut('t', {duration:.2});
18160                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18161                     p.fireEvent("select", this, this.value);
18162                     m.hide();
18163                 }
18164             }
18165         }
18166         
18167         Roo.log('picker set value');
18168         Roo.log(this.getValue());
18169         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18170         m.show(this.el, 'tl-bl?');
18171         ignorehide  = false;
18172         // this will trigger hideMonthPicker..
18173         
18174         
18175         // hidden the day picker
18176         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18177         
18178         
18179         
18180       
18181         
18182         p.showMonthPicker.defer(100, p);
18183     
18184         
18185        
18186     },
18187
18188     beforeBlur : function(){
18189         var v = this.parseDate(this.getRawValue());
18190         if(v){
18191             this.setValue(v);
18192         }
18193     }
18194
18195     /** @cfg {Boolean} grow @hide */
18196     /** @cfg {Number} growMin @hide */
18197     /** @cfg {Number} growMax @hide */
18198     /**
18199      * @hide
18200      * @method autoSize
18201      */
18202 });/*
18203  * Based on:
18204  * Ext JS Library 1.1.1
18205  * Copyright(c) 2006-2007, Ext JS, LLC.
18206  *
18207  * Originally Released Under LGPL - original licence link has changed is not relivant.
18208  *
18209  * Fork - LGPL
18210  * <script type="text/javascript">
18211  */
18212  
18213
18214 /**
18215  * @class Roo.form.ComboBox
18216  * @extends Roo.form.TriggerField
18217  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18218  * @constructor
18219  * Create a new ComboBox.
18220  * @param {Object} config Configuration options
18221  */
18222 Roo.form.ComboBox = function(config){
18223     Roo.form.ComboBox.superclass.constructor.call(this, config);
18224     this.addEvents({
18225         /**
18226          * @event expand
18227          * Fires when the dropdown list is expanded
18228              * @param {Roo.form.ComboBox} combo This combo box
18229              */
18230         'expand' : true,
18231         /**
18232          * @event collapse
18233          * Fires when the dropdown list is collapsed
18234              * @param {Roo.form.ComboBox} combo This combo box
18235              */
18236         'collapse' : true,
18237         /**
18238          * @event beforeselect
18239          * Fires before a list item is selected. Return false to cancel the selection.
18240              * @param {Roo.form.ComboBox} combo This combo box
18241              * @param {Roo.data.Record} record The data record returned from the underlying store
18242              * @param {Number} index The index of the selected item in the dropdown list
18243              */
18244         'beforeselect' : true,
18245         /**
18246          * @event select
18247          * Fires when a list item is selected
18248              * @param {Roo.form.ComboBox} combo This combo box
18249              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18250              * @param {Number} index The index of the selected item in the dropdown list
18251              */
18252         'select' : true,
18253         /**
18254          * @event beforequery
18255          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18256          * The event object passed has these properties:
18257              * @param {Roo.form.ComboBox} combo This combo box
18258              * @param {String} query The query
18259              * @param {Boolean} forceAll true to force "all" query
18260              * @param {Boolean} cancel true to cancel the query
18261              * @param {Object} e The query event object
18262              */
18263         'beforequery': true,
18264          /**
18265          * @event add
18266          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18267              * @param {Roo.form.ComboBox} combo This combo box
18268              */
18269         'add' : true,
18270         /**
18271          * @event edit
18272          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18273              * @param {Roo.form.ComboBox} combo This combo box
18274              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18275              */
18276         'edit' : true
18277         
18278         
18279     });
18280     if(this.transform){
18281         this.allowDomMove = false;
18282         var s = Roo.getDom(this.transform);
18283         if(!this.hiddenName){
18284             this.hiddenName = s.name;
18285         }
18286         if(!this.store){
18287             this.mode = 'local';
18288             var d = [], opts = s.options;
18289             for(var i = 0, len = opts.length;i < len; i++){
18290                 var o = opts[i];
18291                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18292                 if(o.selected) {
18293                     this.value = value;
18294                 }
18295                 d.push([value, o.text]);
18296             }
18297             this.store = new Roo.data.SimpleStore({
18298                 'id': 0,
18299                 fields: ['value', 'text'],
18300                 data : d
18301             });
18302             this.valueField = 'value';
18303             this.displayField = 'text';
18304         }
18305         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18306         if(!this.lazyRender){
18307             this.target = true;
18308             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18309             s.parentNode.removeChild(s); // remove it
18310             this.render(this.el.parentNode);
18311         }else{
18312             s.parentNode.removeChild(s); // remove it
18313         }
18314
18315     }
18316     if (this.store) {
18317         this.store = Roo.factory(this.store, Roo.data);
18318     }
18319     
18320     this.selectedIndex = -1;
18321     if(this.mode == 'local'){
18322         if(config.queryDelay === undefined){
18323             this.queryDelay = 10;
18324         }
18325         if(config.minChars === undefined){
18326             this.minChars = 0;
18327         }
18328     }
18329 };
18330
18331 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18332     /**
18333      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18334      */
18335     /**
18336      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18337      * rendering into an Roo.Editor, defaults to false)
18338      */
18339     /**
18340      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18341      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18342      */
18343     /**
18344      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18345      */
18346     /**
18347      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18348      * the dropdown list (defaults to undefined, with no header element)
18349      */
18350
18351      /**
18352      * @cfg {String/Roo.Template} tpl The template to use to render the output
18353      */
18354      
18355     // private
18356     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18357     /**
18358      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18359      */
18360     listWidth: undefined,
18361     /**
18362      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18363      * mode = 'remote' or 'text' if mode = 'local')
18364      */
18365     displayField: undefined,
18366     /**
18367      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18368      * mode = 'remote' or 'value' if mode = 'local'). 
18369      * Note: use of a valueField requires the user make a selection
18370      * in order for a value to be mapped.
18371      */
18372     valueField: undefined,
18373     
18374     
18375     /**
18376      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18377      * field's data value (defaults to the underlying DOM element's name)
18378      */
18379     hiddenName: undefined,
18380     /**
18381      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18382      */
18383     listClass: '',
18384     /**
18385      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18386      */
18387     selectedClass: 'x-combo-selected',
18388     /**
18389      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18390      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18391      * which displays a downward arrow icon).
18392      */
18393     triggerClass : 'x-form-arrow-trigger',
18394     /**
18395      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18396      */
18397     shadow:'sides',
18398     /**
18399      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18400      * anchor positions (defaults to 'tl-bl')
18401      */
18402     listAlign: 'tl-bl?',
18403     /**
18404      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18405      */
18406     maxHeight: 300,
18407     /**
18408      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18409      * query specified by the allQuery config option (defaults to 'query')
18410      */
18411     triggerAction: 'query',
18412     /**
18413      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18414      * (defaults to 4, does not apply if editable = false)
18415      */
18416     minChars : 4,
18417     /**
18418      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18419      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18420      */
18421     typeAhead: false,
18422     /**
18423      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18424      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18425      */
18426     queryDelay: 500,
18427     /**
18428      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18429      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18430      */
18431     pageSize: 0,
18432     /**
18433      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18434      * when editable = true (defaults to false)
18435      */
18436     selectOnFocus:false,
18437     /**
18438      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18439      */
18440     queryParam: 'query',
18441     /**
18442      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18443      * when mode = 'remote' (defaults to 'Loading...')
18444      */
18445     loadingText: 'Loading...',
18446     /**
18447      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18448      */
18449     resizable: false,
18450     /**
18451      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18452      */
18453     handleHeight : 8,
18454     /**
18455      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18456      * traditional select (defaults to true)
18457      */
18458     editable: true,
18459     /**
18460      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18461      */
18462     allQuery: '',
18463     /**
18464      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18465      */
18466     mode: 'remote',
18467     /**
18468      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18469      * listWidth has a higher value)
18470      */
18471     minListWidth : 70,
18472     /**
18473      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18474      * allow the user to set arbitrary text into the field (defaults to false)
18475      */
18476     forceSelection:false,
18477     /**
18478      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18479      * if typeAhead = true (defaults to 250)
18480      */
18481     typeAheadDelay : 250,
18482     /**
18483      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18484      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18485      */
18486     valueNotFoundText : undefined,
18487     /**
18488      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18489      */
18490     blockFocus : false,
18491     
18492     /**
18493      * @cfg {Boolean} disableClear Disable showing of clear button.
18494      */
18495     disableClear : false,
18496     /**
18497      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18498      */
18499     alwaysQuery : false,
18500     
18501     //private
18502     addicon : false,
18503     editicon: false,
18504     
18505     // element that contains real text value.. (when hidden is used..)
18506      
18507     // private
18508     onRender : function(ct, position)
18509     {
18510         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18511         
18512         if(this.hiddenName){
18513             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18514                     'before', true);
18515             this.hiddenField.value =
18516                 this.hiddenValue !== undefined ? this.hiddenValue :
18517                 this.value !== undefined ? this.value : '';
18518
18519             // prevent input submission
18520             this.el.dom.removeAttribute('name');
18521              
18522              
18523         }
18524         
18525         if(Roo.isGecko){
18526             this.el.dom.setAttribute('autocomplete', 'off');
18527         }
18528
18529         var cls = 'x-combo-list';
18530
18531         this.list = new Roo.Layer({
18532             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18533         });
18534
18535         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18536         this.list.setWidth(lw);
18537         this.list.swallowEvent('mousewheel');
18538         this.assetHeight = 0;
18539
18540         if(this.title){
18541             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18542             this.assetHeight += this.header.getHeight();
18543         }
18544
18545         this.innerList = this.list.createChild({cls:cls+'-inner'});
18546         this.innerList.on('mouseover', this.onViewOver, this);
18547         this.innerList.on('mousemove', this.onViewMove, this);
18548         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18549         
18550         if(this.allowBlank && !this.pageSize && !this.disableClear){
18551             this.footer = this.list.createChild({cls:cls+'-ft'});
18552             this.pageTb = new Roo.Toolbar(this.footer);
18553            
18554         }
18555         if(this.pageSize){
18556             this.footer = this.list.createChild({cls:cls+'-ft'});
18557             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18558                     {pageSize: this.pageSize});
18559             
18560         }
18561         
18562         if (this.pageTb && this.allowBlank && !this.disableClear) {
18563             var _this = this;
18564             this.pageTb.add(new Roo.Toolbar.Fill(), {
18565                 cls: 'x-btn-icon x-btn-clear',
18566                 text: '&#160;',
18567                 handler: function()
18568                 {
18569                     _this.collapse();
18570                     _this.clearValue();
18571                     _this.onSelect(false, -1);
18572                 }
18573             });
18574         }
18575         if (this.footer) {
18576             this.assetHeight += this.footer.getHeight();
18577         }
18578         
18579
18580         if(!this.tpl){
18581             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18582         }
18583
18584         this.view = new Roo.View(this.innerList, this.tpl, {
18585             singleSelect:true,
18586             store: this.store,
18587             selectedClass: this.selectedClass
18588         });
18589
18590         this.view.on('click', this.onViewClick, this);
18591
18592         this.store.on('beforeload', this.onBeforeLoad, this);
18593         this.store.on('load', this.onLoad, this);
18594         this.store.on('loadexception', this.onLoadException, this);
18595
18596         if(this.resizable){
18597             this.resizer = new Roo.Resizable(this.list,  {
18598                pinned:true, handles:'se'
18599             });
18600             this.resizer.on('resize', function(r, w, h){
18601                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18602                 this.listWidth = w;
18603                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18604                 this.restrictHeight();
18605             }, this);
18606             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18607         }
18608         if(!this.editable){
18609             this.editable = true;
18610             this.setEditable(false);
18611         }  
18612         
18613         
18614         if (typeof(this.events.add.listeners) != 'undefined') {
18615             
18616             this.addicon = this.wrap.createChild(
18617                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18618        
18619             this.addicon.on('click', function(e) {
18620                 this.fireEvent('add', this);
18621             }, this);
18622         }
18623         if (typeof(this.events.edit.listeners) != 'undefined') {
18624             
18625             this.editicon = this.wrap.createChild(
18626                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18627             if (this.addicon) {
18628                 this.editicon.setStyle('margin-left', '40px');
18629             }
18630             this.editicon.on('click', function(e) {
18631                 
18632                 // we fire even  if inothing is selected..
18633                 this.fireEvent('edit', this, this.lastData );
18634                 
18635             }, this);
18636         }
18637         
18638         
18639         
18640     },
18641
18642     // private
18643     initEvents : function(){
18644         Roo.form.ComboBox.superclass.initEvents.call(this);
18645
18646         this.keyNav = new Roo.KeyNav(this.el, {
18647             "up" : function(e){
18648                 this.inKeyMode = true;
18649                 this.selectPrev();
18650             },
18651
18652             "down" : function(e){
18653                 if(!this.isExpanded()){
18654                     this.onTriggerClick();
18655                 }else{
18656                     this.inKeyMode = true;
18657                     this.selectNext();
18658                 }
18659             },
18660
18661             "enter" : function(e){
18662                 this.onViewClick();
18663                 //return true;
18664             },
18665
18666             "esc" : function(e){
18667                 this.collapse();
18668             },
18669
18670             "tab" : function(e){
18671                 this.onViewClick(false);
18672                 this.fireEvent("specialkey", this, e);
18673                 return true;
18674             },
18675
18676             scope : this,
18677
18678             doRelay : function(foo, bar, hname){
18679                 if(hname == 'down' || this.scope.isExpanded()){
18680                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18681                 }
18682                 return true;
18683             },
18684
18685             forceKeyDown: true
18686         });
18687         this.queryDelay = Math.max(this.queryDelay || 10,
18688                 this.mode == 'local' ? 10 : 250);
18689         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18690         if(this.typeAhead){
18691             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18692         }
18693         if(this.editable !== false){
18694             this.el.on("keyup", this.onKeyUp, this);
18695         }
18696         if(this.forceSelection){
18697             this.on('blur', this.doForce, this);
18698         }
18699     },
18700
18701     onDestroy : function(){
18702         if(this.view){
18703             this.view.setStore(null);
18704             this.view.el.removeAllListeners();
18705             this.view.el.remove();
18706             this.view.purgeListeners();
18707         }
18708         if(this.list){
18709             this.list.destroy();
18710         }
18711         if(this.store){
18712             this.store.un('beforeload', this.onBeforeLoad, this);
18713             this.store.un('load', this.onLoad, this);
18714             this.store.un('loadexception', this.onLoadException, this);
18715         }
18716         Roo.form.ComboBox.superclass.onDestroy.call(this);
18717     },
18718
18719     // private
18720     fireKey : function(e){
18721         if(e.isNavKeyPress() && !this.list.isVisible()){
18722             this.fireEvent("specialkey", this, e);
18723         }
18724     },
18725
18726     // private
18727     onResize: function(w, h){
18728         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18729         
18730         if(typeof w != 'number'){
18731             // we do not handle it!?!?
18732             return;
18733         }
18734         var tw = this.trigger.getWidth();
18735         tw += this.addicon ? this.addicon.getWidth() : 0;
18736         tw += this.editicon ? this.editicon.getWidth() : 0;
18737         var x = w - tw;
18738         this.el.setWidth( this.adjustWidth('input', x));
18739             
18740         this.trigger.setStyle('left', x+'px');
18741         
18742         if(this.list && this.listWidth === undefined){
18743             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18744             this.list.setWidth(lw);
18745             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18746         }
18747         
18748     
18749         
18750     },
18751
18752     /**
18753      * Allow or prevent the user from directly editing the field text.  If false is passed,
18754      * the user will only be able to select from the items defined in the dropdown list.  This method
18755      * is the runtime equivalent of setting the 'editable' config option at config time.
18756      * @param {Boolean} value True to allow the user to directly edit the field text
18757      */
18758     setEditable : function(value){
18759         if(value == this.editable){
18760             return;
18761         }
18762         this.editable = value;
18763         if(!value){
18764             this.el.dom.setAttribute('readOnly', true);
18765             this.el.on('mousedown', this.onTriggerClick,  this);
18766             this.el.addClass('x-combo-noedit');
18767         }else{
18768             this.el.dom.setAttribute('readOnly', false);
18769             this.el.un('mousedown', this.onTriggerClick,  this);
18770             this.el.removeClass('x-combo-noedit');
18771         }
18772     },
18773
18774     // private
18775     onBeforeLoad : function(){
18776         if(!this.hasFocus){
18777             return;
18778         }
18779         this.innerList.update(this.loadingText ?
18780                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18781         this.restrictHeight();
18782         this.selectedIndex = -1;
18783     },
18784
18785     // private
18786     onLoad : function(){
18787         if(!this.hasFocus){
18788             return;
18789         }
18790         if(this.store.getCount() > 0){
18791             this.expand();
18792             this.restrictHeight();
18793             if(this.lastQuery == this.allQuery){
18794                 if(this.editable){
18795                     this.el.dom.select();
18796                 }
18797                 if(!this.selectByValue(this.value, true)){
18798                     this.select(0, true);
18799                 }
18800             }else{
18801                 this.selectNext();
18802                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18803                     this.taTask.delay(this.typeAheadDelay);
18804                 }
18805             }
18806         }else{
18807             this.onEmptyResults();
18808         }
18809         //this.el.focus();
18810     },
18811     // private
18812     onLoadException : function()
18813     {
18814         this.collapse();
18815         Roo.log(this.store.reader.jsonData);
18816         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18817             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18818         }
18819         
18820         
18821     },
18822     // private
18823     onTypeAhead : function(){
18824         if(this.store.getCount() > 0){
18825             var r = this.store.getAt(0);
18826             var newValue = r.data[this.displayField];
18827             var len = newValue.length;
18828             var selStart = this.getRawValue().length;
18829             if(selStart != len){
18830                 this.setRawValue(newValue);
18831                 this.selectText(selStart, newValue.length);
18832             }
18833         }
18834     },
18835
18836     // private
18837     onSelect : function(record, index){
18838         if(this.fireEvent('beforeselect', this, record, index) !== false){
18839             this.setFromData(index > -1 ? record.data : false);
18840             this.collapse();
18841             this.fireEvent('select', this, record, index);
18842         }
18843     },
18844
18845     /**
18846      * Returns the currently selected field value or empty string if no value is set.
18847      * @return {String} value The selected value
18848      */
18849     getValue : function(){
18850         if(this.valueField){
18851             return typeof this.value != 'undefined' ? this.value : '';
18852         }
18853         return Roo.form.ComboBox.superclass.getValue.call(this);
18854     },
18855
18856     /**
18857      * Clears any text/value currently set in the field
18858      */
18859     clearValue : function(){
18860         if(this.hiddenField){
18861             this.hiddenField.value = '';
18862         }
18863         this.value = '';
18864         this.setRawValue('');
18865         this.lastSelectionText = '';
18866         
18867     },
18868
18869     /**
18870      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18871      * will be displayed in the field.  If the value does not match the data value of an existing item,
18872      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18873      * Otherwise the field will be blank (although the value will still be set).
18874      * @param {String} value The value to match
18875      */
18876     setValue : function(v){
18877         var text = v;
18878         if(this.valueField){
18879             var r = this.findRecord(this.valueField, v);
18880             if(r){
18881                 text = r.data[this.displayField];
18882             }else if(this.valueNotFoundText !== undefined){
18883                 text = this.valueNotFoundText;
18884             }
18885         }
18886         this.lastSelectionText = text;
18887         if(this.hiddenField){
18888             this.hiddenField.value = v;
18889         }
18890         Roo.form.ComboBox.superclass.setValue.call(this, text);
18891         this.value = v;
18892     },
18893     /**
18894      * @property {Object} the last set data for the element
18895      */
18896     
18897     lastData : false,
18898     /**
18899      * Sets the value of the field based on a object which is related to the record format for the store.
18900      * @param {Object} value the value to set as. or false on reset?
18901      */
18902     setFromData : function(o){
18903         var dv = ''; // display value
18904         var vv = ''; // value value..
18905         this.lastData = o;
18906         if (this.displayField) {
18907             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18908         } else {
18909             // this is an error condition!!!
18910             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18911         }
18912         
18913         if(this.valueField){
18914             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18915         }
18916         if(this.hiddenField){
18917             this.hiddenField.value = vv;
18918             
18919             this.lastSelectionText = dv;
18920             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18921             this.value = vv;
18922             return;
18923         }
18924         // no hidden field.. - we store the value in 'value', but still display
18925         // display field!!!!
18926         this.lastSelectionText = dv;
18927         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18928         this.value = vv;
18929         
18930         
18931     },
18932     // private
18933     reset : function(){
18934         // overridden so that last data is reset..
18935         this.setValue(this.resetValue);
18936         this.originalValue = this.getValue();
18937         this.clearInvalid();
18938         this.lastData = false;
18939         if (this.view) {
18940             this.view.clearSelections();
18941         }
18942     },
18943     // private
18944     findRecord : function(prop, value){
18945         var record;
18946         if(this.store.getCount() > 0){
18947             this.store.each(function(r){
18948                 if(r.data[prop] == value){
18949                     record = r;
18950                     return false;
18951                 }
18952                 return true;
18953             });
18954         }
18955         return record;
18956     },
18957     
18958     getName: function()
18959     {
18960         // returns hidden if it's set..
18961         if (!this.rendered) {return ''};
18962         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18963         
18964     },
18965     // private
18966     onViewMove : function(e, t){
18967         this.inKeyMode = false;
18968     },
18969
18970     // private
18971     onViewOver : function(e, t){
18972         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18973             return;
18974         }
18975         var item = this.view.findItemFromChild(t);
18976         if(item){
18977             var index = this.view.indexOf(item);
18978             this.select(index, false);
18979         }
18980     },
18981
18982     // private
18983     onViewClick : function(doFocus)
18984     {
18985         var index = this.view.getSelectedIndexes()[0];
18986         var r = this.store.getAt(index);
18987         if(r){
18988             this.onSelect(r, index);
18989         }
18990         if(doFocus !== false && !this.blockFocus){
18991             this.el.focus();
18992         }
18993     },
18994
18995     // private
18996     restrictHeight : function(){
18997         this.innerList.dom.style.height = '';
18998         var inner = this.innerList.dom;
18999         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19000         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19001         this.list.beginUpdate();
19002         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19003         this.list.alignTo(this.el, this.listAlign);
19004         this.list.endUpdate();
19005     },
19006
19007     // private
19008     onEmptyResults : function(){
19009         this.collapse();
19010     },
19011
19012     /**
19013      * Returns true if the dropdown list is expanded, else false.
19014      */
19015     isExpanded : function(){
19016         return this.list.isVisible();
19017     },
19018
19019     /**
19020      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19021      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19022      * @param {String} value The data value of the item to select
19023      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19024      * selected item if it is not currently in view (defaults to true)
19025      * @return {Boolean} True if the value matched an item in the list, else false
19026      */
19027     selectByValue : function(v, scrollIntoView){
19028         if(v !== undefined && v !== null){
19029             var r = this.findRecord(this.valueField || this.displayField, v);
19030             if(r){
19031                 this.select(this.store.indexOf(r), scrollIntoView);
19032                 return true;
19033             }
19034         }
19035         return false;
19036     },
19037
19038     /**
19039      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19040      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19041      * @param {Number} index The zero-based index of the list item to select
19042      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19043      * selected item if it is not currently in view (defaults to true)
19044      */
19045     select : function(index, scrollIntoView){
19046         this.selectedIndex = index;
19047         this.view.select(index);
19048         if(scrollIntoView !== false){
19049             var el = this.view.getNode(index);
19050             if(el){
19051                 this.innerList.scrollChildIntoView(el, false);
19052             }
19053         }
19054     },
19055
19056     // private
19057     selectNext : function(){
19058         var ct = this.store.getCount();
19059         if(ct > 0){
19060             if(this.selectedIndex == -1){
19061                 this.select(0);
19062             }else if(this.selectedIndex < ct-1){
19063                 this.select(this.selectedIndex+1);
19064             }
19065         }
19066     },
19067
19068     // private
19069     selectPrev : function(){
19070         var ct = this.store.getCount();
19071         if(ct > 0){
19072             if(this.selectedIndex == -1){
19073                 this.select(0);
19074             }else if(this.selectedIndex != 0){
19075                 this.select(this.selectedIndex-1);
19076             }
19077         }
19078     },
19079
19080     // private
19081     onKeyUp : function(e){
19082         if(this.editable !== false && !e.isSpecialKey()){
19083             this.lastKey = e.getKey();
19084             this.dqTask.delay(this.queryDelay);
19085         }
19086     },
19087
19088     // private
19089     validateBlur : function(){
19090         return !this.list || !this.list.isVisible();   
19091     },
19092
19093     // private
19094     initQuery : function(){
19095         this.doQuery(this.getRawValue());
19096     },
19097
19098     // private
19099     doForce : function(){
19100         if(this.el.dom.value.length > 0){
19101             this.el.dom.value =
19102                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19103              
19104         }
19105     },
19106
19107     /**
19108      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19109      * query allowing the query action to be canceled if needed.
19110      * @param {String} query The SQL query to execute
19111      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19112      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19113      * saved in the current store (defaults to false)
19114      */
19115     doQuery : function(q, forceAll){
19116         if(q === undefined || q === null){
19117             q = '';
19118         }
19119         var qe = {
19120             query: q,
19121             forceAll: forceAll,
19122             combo: this,
19123             cancel:false
19124         };
19125         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19126             return false;
19127         }
19128         q = qe.query;
19129         forceAll = qe.forceAll;
19130         if(forceAll === true || (q.length >= this.minChars)){
19131             if(this.lastQuery != q || this.alwaysQuery){
19132                 this.lastQuery = q;
19133                 if(this.mode == 'local'){
19134                     this.selectedIndex = -1;
19135                     if(forceAll){
19136                         this.store.clearFilter();
19137                     }else{
19138                         this.store.filter(this.displayField, q);
19139                     }
19140                     this.onLoad();
19141                 }else{
19142                     this.store.baseParams[this.queryParam] = q;
19143                     this.store.load({
19144                         params: this.getParams(q)
19145                     });
19146                     this.expand();
19147                 }
19148             }else{
19149                 this.selectedIndex = -1;
19150                 this.onLoad();   
19151             }
19152         }
19153     },
19154
19155     // private
19156     getParams : function(q){
19157         var p = {};
19158         //p[this.queryParam] = q;
19159         if(this.pageSize){
19160             p.start = 0;
19161             p.limit = this.pageSize;
19162         }
19163         return p;
19164     },
19165
19166     /**
19167      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19168      */
19169     collapse : function(){
19170         if(!this.isExpanded()){
19171             return;
19172         }
19173         this.list.hide();
19174         Roo.get(document).un('mousedown', this.collapseIf, this);
19175         Roo.get(document).un('mousewheel', this.collapseIf, this);
19176         if (!this.editable) {
19177             Roo.get(document).un('keydown', this.listKeyPress, this);
19178         }
19179         this.fireEvent('collapse', this);
19180     },
19181
19182     // private
19183     collapseIf : function(e){
19184         if(!e.within(this.wrap) && !e.within(this.list)){
19185             this.collapse();
19186         }
19187     },
19188
19189     /**
19190      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19191      */
19192     expand : function(){
19193         if(this.isExpanded() || !this.hasFocus){
19194             return;
19195         }
19196         this.list.alignTo(this.el, this.listAlign);
19197         this.list.show();
19198         Roo.get(document).on('mousedown', this.collapseIf, this);
19199         Roo.get(document).on('mousewheel', this.collapseIf, this);
19200         if (!this.editable) {
19201             Roo.get(document).on('keydown', this.listKeyPress, this);
19202         }
19203         
19204         this.fireEvent('expand', this);
19205     },
19206
19207     // private
19208     // Implements the default empty TriggerField.onTriggerClick function
19209     onTriggerClick : function(){
19210         if(this.disabled){
19211             return;
19212         }
19213         if(this.isExpanded()){
19214             this.collapse();
19215             if (!this.blockFocus) {
19216                 this.el.focus();
19217             }
19218             
19219         }else {
19220             this.hasFocus = true;
19221             if(this.triggerAction == 'all') {
19222                 this.doQuery(this.allQuery, true);
19223             } else {
19224                 this.doQuery(this.getRawValue());
19225             }
19226             if (!this.blockFocus) {
19227                 this.el.focus();
19228             }
19229         }
19230     },
19231     listKeyPress : function(e)
19232     {
19233         //Roo.log('listkeypress');
19234         // scroll to first matching element based on key pres..
19235         if (e.isSpecialKey()) {
19236             return false;
19237         }
19238         var k = String.fromCharCode(e.getKey()).toUpperCase();
19239         //Roo.log(k);
19240         var match  = false;
19241         var csel = this.view.getSelectedNodes();
19242         var cselitem = false;
19243         if (csel.length) {
19244             var ix = this.view.indexOf(csel[0]);
19245             cselitem  = this.store.getAt(ix);
19246             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19247                 cselitem = false;
19248             }
19249             
19250         }
19251         
19252         this.store.each(function(v) { 
19253             if (cselitem) {
19254                 // start at existing selection.
19255                 if (cselitem.id == v.id) {
19256                     cselitem = false;
19257                 }
19258                 return;
19259             }
19260                 
19261             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19262                 match = this.store.indexOf(v);
19263                 return false;
19264             }
19265         }, this);
19266         
19267         if (match === false) {
19268             return true; // no more action?
19269         }
19270         // scroll to?
19271         this.view.select(match);
19272         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19273         sn.scrollIntoView(sn.dom.parentNode, false);
19274     } 
19275
19276     /** 
19277     * @cfg {Boolean} grow 
19278     * @hide 
19279     */
19280     /** 
19281     * @cfg {Number} growMin 
19282     * @hide 
19283     */
19284     /** 
19285     * @cfg {Number} growMax 
19286     * @hide 
19287     */
19288     /**
19289      * @hide
19290      * @method autoSize
19291      */
19292 });/*
19293  * Copyright(c) 2010-2012, Roo J Solutions Limited
19294  *
19295  * Licence LGPL
19296  *
19297  */
19298
19299 /**
19300  * @class Roo.form.ComboBoxArray
19301  * @extends Roo.form.TextField
19302  * A facebook style adder... for lists of email / people / countries  etc...
19303  * pick multiple items from a combo box, and shows each one.
19304  *
19305  *  Fred [x]  Brian [x]  [Pick another |v]
19306  *
19307  *
19308  *  For this to work: it needs various extra information
19309  *    - normal combo problay has
19310  *      name, hiddenName
19311  *    + displayField, valueField
19312  *
19313  *    For our purpose...
19314  *
19315  *
19316  *   If we change from 'extends' to wrapping...
19317  *   
19318  *  
19319  *
19320  
19321  
19322  * @constructor
19323  * Create a new ComboBoxArray.
19324  * @param {Object} config Configuration options
19325  */
19326  
19327
19328 Roo.form.ComboBoxArray = function(config)
19329 {
19330     this.addEvents({
19331         /**
19332          * @event beforeremove
19333          * Fires before remove the value from the list
19334              * @param {Roo.form.ComboBoxArray} _self This combo box array
19335              * @param {Roo.form.ComboBoxArray.Item} item removed item
19336              */
19337         'beforeremove' : true,
19338         /**
19339          * @event remove
19340          * Fires when remove the value from the list
19341              * @param {Roo.form.ComboBoxArray} _self This combo box array
19342              * @param {Roo.form.ComboBoxArray.Item} item removed item
19343              */
19344         'remove' : true
19345         
19346         
19347     });
19348     
19349     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19350     
19351     this.items = new Roo.util.MixedCollection(false);
19352     
19353     // construct the child combo...
19354     
19355     
19356     
19357     
19358    
19359     
19360 }
19361
19362  
19363 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19364
19365     /**
19366      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19367      */
19368     
19369     lastData : false,
19370     
19371     // behavies liek a hiddne field
19372     inputType:      'hidden',
19373     /**
19374      * @cfg {Number} width The width of the box that displays the selected element
19375      */ 
19376     width:          300,
19377
19378     
19379     
19380     /**
19381      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19382      */
19383     name : false,
19384     /**
19385      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19386      */
19387     hiddenName : false,
19388       /**
19389      * @cfg {String} seperator    The value seperator normally ',' 
19390      */
19391     seperator : ',',
19392     
19393     // private the array of items that are displayed..
19394     items  : false,
19395     // private - the hidden field el.
19396     hiddenEl : false,
19397     // private - the filed el..
19398     el : false,
19399     
19400     //validateValue : function() { return true; }, // all values are ok!
19401     //onAddClick: function() { },
19402     
19403     onRender : function(ct, position) 
19404     {
19405         
19406         // create the standard hidden element
19407         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19408         
19409         
19410         // give fake names to child combo;
19411         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19412         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19413         
19414         this.combo = Roo.factory(this.combo, Roo.form);
19415         this.combo.onRender(ct, position);
19416         if (typeof(this.combo.width) != 'undefined') {
19417             this.combo.onResize(this.combo.width,0);
19418         }
19419         
19420         this.combo.initEvents();
19421         
19422         // assigned so form know we need to do this..
19423         this.store          = this.combo.store;
19424         this.valueField     = this.combo.valueField;
19425         this.displayField   = this.combo.displayField ;
19426         
19427         
19428         this.combo.wrap.addClass('x-cbarray-grp');
19429         
19430         var cbwrap = this.combo.wrap.createChild(
19431             {tag: 'div', cls: 'x-cbarray-cb'},
19432             this.combo.el.dom
19433         );
19434         
19435              
19436         this.hiddenEl = this.combo.wrap.createChild({
19437             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19438         });
19439         this.el = this.combo.wrap.createChild({
19440             tag: 'input',  type:'hidden' , name: this.name, value : ''
19441         });
19442          //   this.el.dom.removeAttribute("name");
19443         
19444         
19445         this.outerWrap = this.combo.wrap;
19446         this.wrap = cbwrap;
19447         
19448         this.outerWrap.setWidth(this.width);
19449         this.outerWrap.dom.removeChild(this.el.dom);
19450         
19451         this.wrap.dom.appendChild(this.el.dom);
19452         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19453         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19454         
19455         this.combo.trigger.setStyle('position','relative');
19456         this.combo.trigger.setStyle('left', '0px');
19457         this.combo.trigger.setStyle('top', '2px');
19458         
19459         this.combo.el.setStyle('vertical-align', 'text-bottom');
19460         
19461         //this.trigger.setStyle('vertical-align', 'top');
19462         
19463         // this should use the code from combo really... on('add' ....)
19464         if (this.adder) {
19465             
19466         
19467             this.adder = this.outerWrap.createChild(
19468                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19469             var _t = this;
19470             this.adder.on('click', function(e) {
19471                 _t.fireEvent('adderclick', this, e);
19472             }, _t);
19473         }
19474         //var _t = this;
19475         //this.adder.on('click', this.onAddClick, _t);
19476         
19477         
19478         this.combo.on('select', function(cb, rec, ix) {
19479             this.addItem(rec.data);
19480             
19481             cb.setValue('');
19482             cb.el.dom.value = '';
19483             //cb.lastData = rec.data;
19484             // add to list
19485             
19486         }, this);
19487         
19488         
19489     },
19490     
19491     
19492     getName: function()
19493     {
19494         // returns hidden if it's set..
19495         if (!this.rendered) {return ''};
19496         return  this.hiddenName ? this.hiddenName : this.name;
19497         
19498     },
19499     
19500     
19501     onResize: function(w, h){
19502         
19503         return;
19504         // not sure if this is needed..
19505         //this.combo.onResize(w,h);
19506         
19507         if(typeof w != 'number'){
19508             // we do not handle it!?!?
19509             return;
19510         }
19511         var tw = this.combo.trigger.getWidth();
19512         tw += this.addicon ? this.addicon.getWidth() : 0;
19513         tw += this.editicon ? this.editicon.getWidth() : 0;
19514         var x = w - tw;
19515         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19516             
19517         this.combo.trigger.setStyle('left', '0px');
19518         
19519         if(this.list && this.listWidth === undefined){
19520             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19521             this.list.setWidth(lw);
19522             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19523         }
19524         
19525     
19526         
19527     },
19528     
19529     addItem: function(rec)
19530     {
19531         var valueField = this.combo.valueField;
19532         var displayField = this.combo.displayField;
19533         
19534         if (this.items.indexOfKey(rec[valueField]) > -1) {
19535             //console.log("GOT " + rec.data.id);
19536             return;
19537         }
19538         
19539         var x = new Roo.form.ComboBoxArray.Item({
19540             //id : rec[this.idField],
19541             data : rec,
19542             displayField : displayField ,
19543             tipField : displayField ,
19544             cb : this
19545         });
19546         // use the 
19547         this.items.add(rec[valueField],x);
19548         // add it before the element..
19549         this.updateHiddenEl();
19550         x.render(this.outerWrap, this.wrap.dom);
19551         // add the image handler..
19552     },
19553     
19554     updateHiddenEl : function()
19555     {
19556         this.validate();
19557         if (!this.hiddenEl) {
19558             return;
19559         }
19560         var ar = [];
19561         var idField = this.combo.valueField;
19562         
19563         this.items.each(function(f) {
19564             ar.push(f.data[idField]);
19565         });
19566         this.hiddenEl.dom.value = ar.join(this.seperator);
19567         this.validate();
19568     },
19569     
19570     reset : function()
19571     {
19572         this.items.clear();
19573         
19574         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19575            el.remove();
19576         });
19577         
19578         this.el.dom.value = '';
19579         if (this.hiddenEl) {
19580             this.hiddenEl.dom.value = '';
19581         }
19582         
19583     },
19584     getValue: function()
19585     {
19586         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19587     },
19588     setValue: function(v) // not a valid action - must use addItems..
19589     {
19590         
19591         this.reset();
19592          
19593         if (this.store.isLocal && (typeof(v) == 'string')) {
19594             // then we can use the store to find the values..
19595             // comma seperated at present.. this needs to allow JSON based encoding..
19596             this.hiddenEl.value  = v;
19597             var v_ar = [];
19598             Roo.each(v.split(this.seperator), function(k) {
19599                 Roo.log("CHECK " + this.valueField + ',' + k);
19600                 var li = this.store.query(this.valueField, k);
19601                 if (!li.length) {
19602                     return;
19603                 }
19604                 var add = {};
19605                 add[this.valueField] = k;
19606                 add[this.displayField] = li.item(0).data[this.displayField];
19607                 
19608                 this.addItem(add);
19609             }, this) 
19610              
19611         }
19612         if (typeof(v) == 'object' ) {
19613             // then let's assume it's an array of objects..
19614             Roo.each(v, function(l) {
19615                 var add = l;
19616                 if (typeof(l) == 'string') {
19617                     add = {};
19618                     add[this.valueField] = l;
19619                     add[this.displayField] = l
19620                 }
19621                 this.addItem(add);
19622             }, this);
19623              
19624         }
19625         
19626         
19627     },
19628     setFromData: function(v)
19629     {
19630         // this recieves an object, if setValues is called.
19631         this.reset();
19632         this.el.dom.value = v[this.displayField];
19633         this.hiddenEl.dom.value = v[this.valueField];
19634         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19635             return;
19636         }
19637         var kv = v[this.valueField];
19638         var dv = v[this.displayField];
19639         kv = typeof(kv) != 'string' ? '' : kv;
19640         dv = typeof(dv) != 'string' ? '' : dv;
19641         
19642         
19643         var keys = kv.split(this.seperator);
19644         var display = dv.split(this.seperator);
19645         for (var i = 0 ; i < keys.length; i++) {
19646             add = {};
19647             add[this.valueField] = keys[i];
19648             add[this.displayField] = display[i];
19649             this.addItem(add);
19650         }
19651       
19652         
19653     },
19654     
19655     /**
19656      * Validates the combox array value
19657      * @return {Boolean} True if the value is valid, else false
19658      */
19659     validate : function(){
19660         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19661             this.clearInvalid();
19662             return true;
19663         }
19664         return false;
19665     },
19666     
19667     validateValue : function(value){
19668         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19669         
19670     },
19671     
19672     /*@
19673      * overide
19674      * 
19675      */
19676     isDirty : function() {
19677         if(this.disabled) {
19678             return false;
19679         }
19680         
19681         try {
19682             var d = Roo.decode(String(this.originalValue));
19683         } catch (e) {
19684             return String(this.getValue()) !== String(this.originalValue);
19685         }
19686         
19687         var originalValue = [];
19688         
19689         for (var i = 0; i < d.length; i++){
19690             originalValue.push(d[i][this.valueField]);
19691         }
19692         
19693         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19694         
19695     }
19696     
19697 });
19698
19699
19700
19701 /**
19702  * @class Roo.form.ComboBoxArray.Item
19703  * @extends Roo.BoxComponent
19704  * A selected item in the list
19705  *  Fred [x]  Brian [x]  [Pick another |v]
19706  * 
19707  * @constructor
19708  * Create a new item.
19709  * @param {Object} config Configuration options
19710  */
19711  
19712 Roo.form.ComboBoxArray.Item = function(config) {
19713     config.id = Roo.id();
19714     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19715 }
19716
19717 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19718     data : {},
19719     cb: false,
19720     displayField : false,
19721     tipField : false,
19722     
19723     
19724     defaultAutoCreate : {
19725         tag: 'div',
19726         cls: 'x-cbarray-item',
19727         cn : [ 
19728             { tag: 'div' },
19729             {
19730                 tag: 'img',
19731                 width:16,
19732                 height : 16,
19733                 src : Roo.BLANK_IMAGE_URL ,
19734                 align: 'center'
19735             }
19736         ]
19737         
19738     },
19739     
19740  
19741     onRender : function(ct, position)
19742     {
19743         Roo.form.Field.superclass.onRender.call(this, ct, position);
19744         
19745         if(!this.el){
19746             var cfg = this.getAutoCreate();
19747             this.el = ct.createChild(cfg, position);
19748         }
19749         
19750         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19751         
19752         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19753             this.cb.renderer(this.data) :
19754             String.format('{0}',this.data[this.displayField]);
19755         
19756             
19757         this.el.child('div').dom.setAttribute('qtip',
19758                         String.format('{0}',this.data[this.tipField])
19759         );
19760         
19761         this.el.child('img').on('click', this.remove, this);
19762         
19763     },
19764    
19765     remove : function()
19766     {
19767         if(this.cb.disabled){
19768             return;
19769         }
19770         
19771         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19772             this.cb.items.remove(this);
19773             this.el.child('img').un('click', this.remove, this);
19774             this.el.remove();
19775             this.cb.updateHiddenEl();
19776
19777             this.cb.fireEvent('remove', this.cb, this);
19778         }
19779         
19780     }
19781 });/*
19782  * RooJS Library 1.1.1
19783  * Copyright(c) 2008-2011  Alan Knowles
19784  *
19785  * License - LGPL
19786  */
19787  
19788
19789 /**
19790  * @class Roo.form.ComboNested
19791  * @extends Roo.form.ComboBox
19792  * A combobox for that allows selection of nested items in a list,
19793  * eg.
19794  *
19795  *  Book
19796  *    -> red
19797  *    -> green
19798  *  Table
19799  *    -> square
19800  *      ->red
19801  *      ->green
19802  *    -> rectangle
19803  *      ->green
19804  *      
19805  * 
19806  * @constructor
19807  * Create a new ComboNested
19808  * @param {Object} config Configuration options
19809  */
19810 Roo.form.ComboNested = function(config){
19811     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19812     // should verify some data...
19813     // like
19814     // hiddenName = required..
19815     // displayField = required
19816     // valudField == required
19817     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19818     var _t = this;
19819     Roo.each(req, function(e) {
19820         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19821             throw "Roo.form.ComboNested : missing value for: " + e;
19822         }
19823     });
19824      
19825     
19826 };
19827
19828 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19829    
19830     /*
19831      * @config {Number} max Number of columns to show
19832      */
19833     
19834     maxColumns : 3,
19835    
19836     list : null, // the outermost div..
19837     innerLists : null, // the
19838     views : null,
19839     stores : null,
19840     // private
19841     loadingChildren : false,
19842     
19843     onRender : function(ct, position)
19844     {
19845         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19846         
19847         if(this.hiddenName){
19848             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19849                     'before', true);
19850             this.hiddenField.value =
19851                 this.hiddenValue !== undefined ? this.hiddenValue :
19852                 this.value !== undefined ? this.value : '';
19853
19854             // prevent input submission
19855             this.el.dom.removeAttribute('name');
19856              
19857              
19858         }
19859         
19860         if(Roo.isGecko){
19861             this.el.dom.setAttribute('autocomplete', 'off');
19862         }
19863
19864         var cls = 'x-combo-list';
19865
19866         this.list = new Roo.Layer({
19867             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19868         });
19869
19870         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19871         this.list.setWidth(lw);
19872         this.list.swallowEvent('mousewheel');
19873         this.assetHeight = 0;
19874
19875         if(this.title){
19876             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19877             this.assetHeight += this.header.getHeight();
19878         }
19879         this.innerLists = [];
19880         this.views = [];
19881         this.stores = [];
19882         for (var i =0 ; i < this.maxColumns; i++) {
19883             this.onRenderList( cls, i);
19884         }
19885         
19886         // always needs footer, as we are going to have an 'OK' button.
19887         this.footer = this.list.createChild({cls:cls+'-ft'});
19888         this.pageTb = new Roo.Toolbar(this.footer);  
19889         var _this = this;
19890         this.pageTb.add(  {
19891             
19892             text: 'Done',
19893             handler: function()
19894             {
19895                 _this.collapse();
19896             }
19897         });
19898         
19899         if ( this.allowBlank && !this.disableClear) {
19900             
19901             this.pageTb.add(new Roo.Toolbar.Fill(), {
19902                 cls: 'x-btn-icon x-btn-clear',
19903                 text: '&#160;',
19904                 handler: function()
19905                 {
19906                     _this.collapse();
19907                     _this.clearValue();
19908                     _this.onSelect(false, -1);
19909                 }
19910             });
19911         }
19912         if (this.footer) {
19913             this.assetHeight += this.footer.getHeight();
19914         }
19915         
19916     },
19917     onRenderList : function (  cls, i)
19918     {
19919         
19920         var lw = Math.floor(
19921                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19922         );
19923         
19924         this.list.setWidth(lw); // default to '1'
19925
19926         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19927         //il.on('mouseover', this.onViewOver, this, { list:  i });
19928         //il.on('mousemove', this.onViewMove, this, { list:  i });
19929         il.setWidth(lw);
19930         il.setStyle({ 'overflow-x' : 'hidden'});
19931
19932         if(!this.tpl){
19933             this.tpl = new Roo.Template({
19934                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19935                 isEmpty: function (value, allValues) {
19936                     //Roo.log(value);
19937                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19938                     return dl ? 'has-children' : 'no-children'
19939                 }
19940             });
19941         }
19942         
19943         var store  = this.store;
19944         if (i > 0) {
19945             store  = new Roo.data.SimpleStore({
19946                 //fields : this.store.reader.meta.fields,
19947                 reader : this.store.reader,
19948                 data : [ ]
19949             });
19950         }
19951         this.stores[i]  = store;
19952                   
19953         var view = this.views[i] = new Roo.View(
19954             il,
19955             this.tpl,
19956             {
19957                 singleSelect:true,
19958                 store: store,
19959                 selectedClass: this.selectedClass
19960             }
19961         );
19962         view.getEl().setWidth(lw);
19963         view.getEl().setStyle({
19964             position: i < 1 ? 'relative' : 'absolute',
19965             top: 0,
19966             left: (i * lw ) + 'px',
19967             display : i > 0 ? 'none' : 'block'
19968         });
19969         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
19970         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
19971         //view.on('click', this.onViewClick, this, { list : i });
19972
19973         store.on('beforeload', this.onBeforeLoad, this);
19974         store.on('load',  this.onLoad, this, { list  : i});
19975         store.on('loadexception', this.onLoadException, this);
19976
19977         // hide the other vies..
19978         
19979         
19980         
19981     },
19982       
19983     restrictHeight : function()
19984     {
19985         var mh = 0;
19986         Roo.each(this.innerLists, function(il,i) {
19987             var el = this.views[i].getEl();
19988             el.dom.style.height = '';
19989             var inner = el.dom;
19990             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
19991             // only adjust heights on other ones..
19992             mh = Math.max(h, mh);
19993             if (i < 1) {
19994                 
19995                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19996                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19997                
19998             }
19999             
20000             
20001         }, this);
20002         
20003         this.list.beginUpdate();
20004         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20005         this.list.alignTo(this.el, this.listAlign);
20006         this.list.endUpdate();
20007         
20008     },
20009      
20010     
20011     // -- store handlers..
20012     // private
20013     onBeforeLoad : function()
20014     {
20015         if(!this.hasFocus){
20016             return;
20017         }
20018         this.innerLists[0].update(this.loadingText ?
20019                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20020         this.restrictHeight();
20021         this.selectedIndex = -1;
20022     },
20023     // private
20024     onLoad : function(a,b,c,d)
20025     {
20026         if (!this.loadingChildren) {
20027             // then we are loading the top level. - hide the children
20028             for (var i = 1;i < this.views.length; i++) {
20029                 this.views[i].getEl().setStyle({ display : 'none' });
20030             }
20031             var lw = Math.floor(
20032                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20033             );
20034         
20035              this.list.setWidth(lw); // default to '1'
20036
20037             
20038         }
20039         if(!this.hasFocus){
20040             return;
20041         }
20042         
20043         if(this.store.getCount() > 0) {
20044             this.expand();
20045             this.restrictHeight();   
20046         } else {
20047             this.onEmptyResults();
20048         }
20049         
20050         if (!this.loadingChildren) {
20051             this.selectActive();
20052         }
20053         /*
20054         this.stores[1].loadData([]);
20055         this.stores[2].loadData([]);
20056         this.views
20057         */    
20058     
20059         //this.el.focus();
20060     },
20061     
20062     
20063     // private
20064     onLoadException : function()
20065     {
20066         this.collapse();
20067         Roo.log(this.store.reader.jsonData);
20068         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20069             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20070         }
20071         
20072         
20073     },
20074     // no cleaning of leading spaces on blur here.
20075     cleanLeadingSpace : function(e) { },
20076     
20077
20078     onSelectChange : function (view, sels, opts )
20079     {
20080         var ix = view.getSelectedIndexes();
20081          
20082         if (opts.list > this.maxColumns - 2) {
20083             if (view.store.getCount()<  1) {
20084                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20085
20086             } else  {
20087                 if (ix.length) {
20088                     // used to clear ?? but if we are loading unselected 
20089                     this.setFromData(view.store.getAt(ix[0]).data);
20090                 }
20091                 
20092             }
20093             
20094             return;
20095         }
20096         
20097         if (!ix.length) {
20098             // this get's fired when trigger opens..
20099            // this.setFromData({});
20100             var str = this.stores[opts.list+1];
20101             str.data.clear(); // removeall wihtout the fire events..
20102             return;
20103         }
20104         
20105         var rec = view.store.getAt(ix[0]);
20106          
20107         this.setFromData(rec.data);
20108         this.fireEvent('select', this, rec, ix[0]);
20109         
20110         var lw = Math.floor(
20111              (
20112                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20113              ) / this.maxColumns
20114         );
20115         this.loadingChildren = true;
20116         this.stores[opts.list+1].loadDataFromChildren( rec );
20117         this.loadingChildren = false;
20118         var dl = this.stores[opts.list+1]. getTotalCount();
20119         
20120         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20121         
20122         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20123         for (var i = opts.list+2; i < this.views.length;i++) {
20124             this.views[i].getEl().setStyle({ display : 'none' });
20125         }
20126         
20127         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20128         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20129         
20130         if (this.isLoading) {
20131            // this.selectActive(opts.list);
20132         }
20133          
20134     },
20135     
20136     
20137     
20138     
20139     onDoubleClick : function()
20140     {
20141         this.collapse(); //??
20142     },
20143     
20144      
20145     
20146     
20147     
20148     // private
20149     recordToStack : function(store, prop, value, stack)
20150     {
20151         var cstore = new Roo.data.SimpleStore({
20152             //fields : this.store.reader.meta.fields, // we need array reader.. for
20153             reader : this.store.reader,
20154             data : [ ]
20155         });
20156         var _this = this;
20157         var record  = false;
20158         var srec = false;
20159         if(store.getCount() < 1){
20160             return false;
20161         }
20162         store.each(function(r){
20163             if(r.data[prop] == value){
20164                 record = r;
20165             srec = r;
20166                 return false;
20167             }
20168             if (r.data.cn && r.data.cn.length) {
20169                 cstore.loadDataFromChildren( r);
20170                 var cret = _this.recordToStack(cstore, prop, value, stack);
20171                 if (cret !== false) {
20172                     record = cret;
20173                     srec = r;
20174                     return false;
20175                 }
20176             }
20177              
20178             return true;
20179         });
20180         if (record == false) {
20181             return false
20182         }
20183         stack.unshift(srec);
20184         return record;
20185     },
20186     
20187     /*
20188      * find the stack of stores that match our value.
20189      *
20190      * 
20191      */
20192     
20193     selectActive : function ()
20194     {
20195         // if store is not loaded, then we will need to wait for that to happen first.
20196         var stack = [];
20197         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20198         for (var i = 0; i < stack.length; i++ ) {
20199             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20200         }
20201         
20202     }
20203         
20204          
20205     
20206     
20207     
20208     
20209 });/*
20210  * Based on:
20211  * Ext JS Library 1.1.1
20212  * Copyright(c) 2006-2007, Ext JS, LLC.
20213  *
20214  * Originally Released Under LGPL - original licence link has changed is not relivant.
20215  *
20216  * Fork - LGPL
20217  * <script type="text/javascript">
20218  */
20219 /**
20220  * @class Roo.form.Checkbox
20221  * @extends Roo.form.Field
20222  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20223  * @constructor
20224  * Creates a new Checkbox
20225  * @param {Object} config Configuration options
20226  */
20227 Roo.form.Checkbox = function(config){
20228     Roo.form.Checkbox.superclass.constructor.call(this, config);
20229     this.addEvents({
20230         /**
20231          * @event check
20232          * Fires when the checkbox is checked or unchecked.
20233              * @param {Roo.form.Checkbox} this This checkbox
20234              * @param {Boolean} checked The new checked value
20235              */
20236         check : true
20237     });
20238 };
20239
20240 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20241     /**
20242      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20243      */
20244     focusClass : undefined,
20245     /**
20246      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20247      */
20248     fieldClass: "x-form-field",
20249     /**
20250      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20251      */
20252     checked: false,
20253     /**
20254      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20255      * {tag: "input", type: "checkbox", autocomplete: "off"})
20256      */
20257     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20258     /**
20259      * @cfg {String} boxLabel The text that appears beside the checkbox
20260      */
20261     boxLabel : "",
20262     /**
20263      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20264      */  
20265     inputValue : '1',
20266     /**
20267      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20268      */
20269      valueOff: '0', // value when not checked..
20270
20271     actionMode : 'viewEl', 
20272     //
20273     // private
20274     itemCls : 'x-menu-check-item x-form-item',
20275     groupClass : 'x-menu-group-item',
20276     inputType : 'hidden',
20277     
20278     
20279     inSetChecked: false, // check that we are not calling self...
20280     
20281     inputElement: false, // real input element?
20282     basedOn: false, // ????
20283     
20284     isFormField: true, // not sure where this is needed!!!!
20285
20286     onResize : function(){
20287         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20288         if(!this.boxLabel){
20289             this.el.alignTo(this.wrap, 'c-c');
20290         }
20291     },
20292
20293     initEvents : function(){
20294         Roo.form.Checkbox.superclass.initEvents.call(this);
20295         this.el.on("click", this.onClick,  this);
20296         this.el.on("change", this.onClick,  this);
20297     },
20298
20299
20300     getResizeEl : function(){
20301         return this.wrap;
20302     },
20303
20304     getPositionEl : function(){
20305         return this.wrap;
20306     },
20307
20308     // private
20309     onRender : function(ct, position){
20310         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20311         /*
20312         if(this.inputValue !== undefined){
20313             this.el.dom.value = this.inputValue;
20314         }
20315         */
20316         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20317         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20318         var viewEl = this.wrap.createChild({ 
20319             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20320         this.viewEl = viewEl;   
20321         this.wrap.on('click', this.onClick,  this); 
20322         
20323         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20324         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20325         
20326         
20327         
20328         if(this.boxLabel){
20329             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20330         //    viewEl.on('click', this.onClick,  this); 
20331         }
20332         //if(this.checked){
20333             this.setChecked(this.checked);
20334         //}else{
20335             //this.checked = this.el.dom;
20336         //}
20337
20338     },
20339
20340     // private
20341     initValue : Roo.emptyFn,
20342
20343     /**
20344      * Returns the checked state of the checkbox.
20345      * @return {Boolean} True if checked, else false
20346      */
20347     getValue : function(){
20348         if(this.el){
20349             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20350         }
20351         return this.valueOff;
20352         
20353     },
20354
20355         // private
20356     onClick : function(){ 
20357         if (this.disabled) {
20358             return;
20359         }
20360         this.setChecked(!this.checked);
20361
20362         //if(this.el.dom.checked != this.checked){
20363         //    this.setValue(this.el.dom.checked);
20364        // }
20365     },
20366
20367     /**
20368      * Sets the checked state of the checkbox.
20369      * On is always based on a string comparison between inputValue and the param.
20370      * @param {Boolean/String} value - the value to set 
20371      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20372      */
20373     setValue : function(v,suppressEvent){
20374         
20375         
20376         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20377         //if(this.el && this.el.dom){
20378         //    this.el.dom.checked = this.checked;
20379         //    this.el.dom.defaultChecked = this.checked;
20380         //}
20381         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20382         //this.fireEvent("check", this, this.checked);
20383     },
20384     // private..
20385     setChecked : function(state,suppressEvent)
20386     {
20387         if (this.inSetChecked) {
20388             this.checked = state;
20389             return;
20390         }
20391         
20392     
20393         if(this.wrap){
20394             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20395         }
20396         this.checked = state;
20397         if(suppressEvent !== true){
20398             this.fireEvent('check', this, state);
20399         }
20400         this.inSetChecked = true;
20401         this.el.dom.value = state ? this.inputValue : this.valueOff;
20402         this.inSetChecked = false;
20403         
20404     },
20405     // handle setting of hidden value by some other method!!?!?
20406     setFromHidden: function()
20407     {
20408         if(!this.el){
20409             return;
20410         }
20411         //console.log("SET FROM HIDDEN");
20412         //alert('setFrom hidden');
20413         this.setValue(this.el.dom.value);
20414     },
20415     
20416     onDestroy : function()
20417     {
20418         if(this.viewEl){
20419             Roo.get(this.viewEl).remove();
20420         }
20421          
20422         Roo.form.Checkbox.superclass.onDestroy.call(this);
20423     },
20424     
20425     setBoxLabel : function(str)
20426     {
20427         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20428     }
20429
20430 });/*
20431  * Based on:
20432  * Ext JS Library 1.1.1
20433  * Copyright(c) 2006-2007, Ext JS, LLC.
20434  *
20435  * Originally Released Under LGPL - original licence link has changed is not relivant.
20436  *
20437  * Fork - LGPL
20438  * <script type="text/javascript">
20439  */
20440  
20441 /**
20442  * @class Roo.form.Radio
20443  * @extends Roo.form.Checkbox
20444  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20445  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20446  * @constructor
20447  * Creates a new Radio
20448  * @param {Object} config Configuration options
20449  */
20450 Roo.form.Radio = function(){
20451     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20452 };
20453 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20454     inputType: 'radio',
20455
20456     /**
20457      * If this radio is part of a group, it will return the selected value
20458      * @return {String}
20459      */
20460     getGroupValue : function(){
20461         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20462     },
20463     
20464     
20465     onRender : function(ct, position){
20466         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20467         
20468         if(this.inputValue !== undefined){
20469             this.el.dom.value = this.inputValue;
20470         }
20471          
20472         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20473         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20474         //var viewEl = this.wrap.createChild({ 
20475         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20476         //this.viewEl = viewEl;   
20477         //this.wrap.on('click', this.onClick,  this); 
20478         
20479         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20480         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20481         
20482         
20483         
20484         if(this.boxLabel){
20485             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20486         //    viewEl.on('click', this.onClick,  this); 
20487         }
20488          if(this.checked){
20489             this.el.dom.checked =   'checked' ;
20490         }
20491          
20492     } 
20493     
20494     
20495 });//<script type="text/javascript">
20496
20497 /*
20498  * Based  Ext JS Library 1.1.1
20499  * Copyright(c) 2006-2007, Ext JS, LLC.
20500  * LGPL
20501  *
20502  */
20503  
20504 /**
20505  * @class Roo.HtmlEditorCore
20506  * @extends Roo.Component
20507  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20508  *
20509  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20510  */
20511
20512 Roo.HtmlEditorCore = function(config){
20513     
20514     
20515     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20516     
20517     
20518     this.addEvents({
20519         /**
20520          * @event initialize
20521          * Fires when the editor is fully initialized (including the iframe)
20522          * @param {Roo.HtmlEditorCore} this
20523          */
20524         initialize: true,
20525         /**
20526          * @event activate
20527          * Fires when the editor is first receives the focus. Any insertion must wait
20528          * until after this event.
20529          * @param {Roo.HtmlEditorCore} this
20530          */
20531         activate: true,
20532          /**
20533          * @event beforesync
20534          * Fires before the textarea is updated with content from the editor iframe. Return false
20535          * to cancel the sync.
20536          * @param {Roo.HtmlEditorCore} this
20537          * @param {String} html
20538          */
20539         beforesync: true,
20540          /**
20541          * @event beforepush
20542          * Fires before the iframe editor is updated with content from the textarea. Return false
20543          * to cancel the push.
20544          * @param {Roo.HtmlEditorCore} this
20545          * @param {String} html
20546          */
20547         beforepush: true,
20548          /**
20549          * @event sync
20550          * Fires when the textarea is updated with content from the editor iframe.
20551          * @param {Roo.HtmlEditorCore} this
20552          * @param {String} html
20553          */
20554         sync: true,
20555          /**
20556          * @event push
20557          * Fires when the iframe editor is updated with content from the textarea.
20558          * @param {Roo.HtmlEditorCore} this
20559          * @param {String} html
20560          */
20561         push: true,
20562         
20563         /**
20564          * @event editorevent
20565          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20566          * @param {Roo.HtmlEditorCore} this
20567          */
20568         editorevent: true
20569         
20570     });
20571     
20572     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20573     
20574     // defaults : white / black...
20575     this.applyBlacklists();
20576     
20577     
20578     
20579 };
20580
20581
20582 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20583
20584
20585      /**
20586      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20587      */
20588     
20589     owner : false,
20590     
20591      /**
20592      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20593      *                        Roo.resizable.
20594      */
20595     resizable : false,
20596      /**
20597      * @cfg {Number} height (in pixels)
20598      */   
20599     height: 300,
20600    /**
20601      * @cfg {Number} width (in pixels)
20602      */   
20603     width: 500,
20604     
20605     /**
20606      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20607      * 
20608      */
20609     stylesheets: false,
20610     
20611     /**
20612      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20613      */
20614     allowComments: false,
20615     // id of frame..
20616     frameId: false,
20617     
20618     // private properties
20619     validationEvent : false,
20620     deferHeight: true,
20621     initialized : false,
20622     activated : false,
20623     sourceEditMode : false,
20624     onFocus : Roo.emptyFn,
20625     iframePad:3,
20626     hideMode:'offsets',
20627     
20628     clearUp: true,
20629     
20630     // blacklist + whitelisted elements..
20631     black: false,
20632     white: false,
20633      
20634     bodyCls : '',
20635
20636     /**
20637      * Protected method that will not generally be called directly. It
20638      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20639      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20640      */
20641     getDocMarkup : function(){
20642         // body styles..
20643         var st = '';
20644         
20645         // inherit styels from page...?? 
20646         if (this.stylesheets === false) {
20647             
20648             Roo.get(document.head).select('style').each(function(node) {
20649                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20650             });
20651             
20652             Roo.get(document.head).select('link').each(function(node) { 
20653                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20654             });
20655             
20656         } else if (!this.stylesheets.length) {
20657                 // simple..
20658                 st = '<style type="text/css">' +
20659                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20660                    '</style>';
20661         } else {
20662             for (var i in this.stylesheets) { 
20663                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20664             }
20665             
20666         }
20667         
20668         st +=  '<style type="text/css">' +
20669             'IMG { cursor: pointer } ' +
20670         '</style>';
20671
20672         var cls = 'roo-htmleditor-body';
20673         
20674         if(this.bodyCls.length){
20675             cls += ' ' + this.bodyCls;
20676         }
20677         
20678         return '<html><head>' + st  +
20679             //<style type="text/css">' +
20680             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20681             //'</style>' +
20682             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20683     },
20684
20685     // private
20686     onRender : function(ct, position)
20687     {
20688         var _t = this;
20689         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20690         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20691         
20692         
20693         this.el.dom.style.border = '0 none';
20694         this.el.dom.setAttribute('tabIndex', -1);
20695         this.el.addClass('x-hidden hide');
20696         
20697         
20698         
20699         if(Roo.isIE){ // fix IE 1px bogus margin
20700             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20701         }
20702        
20703         
20704         this.frameId = Roo.id();
20705         
20706          
20707         
20708         var iframe = this.owner.wrap.createChild({
20709             tag: 'iframe',
20710             cls: 'form-control', // bootstrap..
20711             id: this.frameId,
20712             name: this.frameId,
20713             frameBorder : 'no',
20714             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20715         }, this.el
20716         );
20717         
20718         
20719         this.iframe = iframe.dom;
20720
20721          this.assignDocWin();
20722         
20723         this.doc.designMode = 'on';
20724        
20725         this.doc.open();
20726         this.doc.write(this.getDocMarkup());
20727         this.doc.close();
20728
20729         
20730         var task = { // must defer to wait for browser to be ready
20731             run : function(){
20732                 //console.log("run task?" + this.doc.readyState);
20733                 this.assignDocWin();
20734                 if(this.doc.body || this.doc.readyState == 'complete'){
20735                     try {
20736                         this.doc.designMode="on";
20737                     } catch (e) {
20738                         return;
20739                     }
20740                     Roo.TaskMgr.stop(task);
20741                     this.initEditor.defer(10, this);
20742                 }
20743             },
20744             interval : 10,
20745             duration: 10000,
20746             scope: this
20747         };
20748         Roo.TaskMgr.start(task);
20749
20750     },
20751
20752     // private
20753     onResize : function(w, h)
20754     {
20755          Roo.log('resize: ' +w + ',' + h );
20756         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20757         if(!this.iframe){
20758             return;
20759         }
20760         if(typeof w == 'number'){
20761             
20762             this.iframe.style.width = w + 'px';
20763         }
20764         if(typeof h == 'number'){
20765             
20766             this.iframe.style.height = h + 'px';
20767             if(this.doc){
20768                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20769             }
20770         }
20771         
20772     },
20773
20774     /**
20775      * Toggles the editor between standard and source edit mode.
20776      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20777      */
20778     toggleSourceEdit : function(sourceEditMode){
20779         
20780         this.sourceEditMode = sourceEditMode === true;
20781         
20782         if(this.sourceEditMode){
20783  
20784             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20785             
20786         }else{
20787             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20788             //this.iframe.className = '';
20789             this.deferFocus();
20790         }
20791         //this.setSize(this.owner.wrap.getSize());
20792         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20793     },
20794
20795     
20796   
20797
20798     /**
20799      * Protected method that will not generally be called directly. If you need/want
20800      * custom HTML cleanup, this is the method you should override.
20801      * @param {String} html The HTML to be cleaned
20802      * return {String} The cleaned HTML
20803      */
20804     cleanHtml : function(html){
20805         html = String(html);
20806         if(html.length > 5){
20807             if(Roo.isSafari){ // strip safari nonsense
20808                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20809             }
20810         }
20811         if(html == '&nbsp;'){
20812             html = '';
20813         }
20814         return html;
20815     },
20816
20817     /**
20818      * HTML Editor -> Textarea
20819      * Protected method that will not generally be called directly. Syncs the contents
20820      * of the editor iframe with the textarea.
20821      */
20822     syncValue : function(){
20823         if(this.initialized){
20824             var bd = (this.doc.body || this.doc.documentElement);
20825             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20826             var html = bd.innerHTML;
20827             if(Roo.isSafari){
20828                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20829                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20830                 if(m && m[1]){
20831                     html = '<div style="'+m[0]+'">' + html + '</div>';
20832                 }
20833             }
20834             html = this.cleanHtml(html);
20835             // fix up the special chars.. normaly like back quotes in word...
20836             // however we do not want to do this with chinese..
20837             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20838                 
20839                 var cc = match.charCodeAt();
20840
20841                 // Get the character value, handling surrogate pairs
20842                 if (match.length == 2) {
20843                     // It's a surrogate pair, calculate the Unicode code point
20844                     var high = match.charCodeAt(0) - 0xD800;
20845                     var low  = match.charCodeAt(1) - 0xDC00;
20846                     cc = (high * 0x400) + low + 0x10000;
20847                 }  else if (
20848                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20849                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20850                     (cc >= 0xf900 && cc < 0xfb00 )
20851                 ) {
20852                         return match;
20853                 }  
20854          
20855                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20856                 return "&#" + cc + ";";
20857                 
20858                 
20859             });
20860             
20861             
20862              
20863             if(this.owner.fireEvent('beforesync', this, html) !== false){
20864                 this.el.dom.value = html;
20865                 this.owner.fireEvent('sync', this, html);
20866             }
20867         }
20868     },
20869
20870     /**
20871      * Protected method that will not generally be called directly. Pushes the value of the textarea
20872      * into the iframe editor.
20873      */
20874     pushValue : function(){
20875         if(this.initialized){
20876             var v = this.el.dom.value.trim();
20877             
20878 //            if(v.length < 1){
20879 //                v = '&#160;';
20880 //            }
20881             
20882             if(this.owner.fireEvent('beforepush', this, v) !== false){
20883                 var d = (this.doc.body || this.doc.documentElement);
20884                 d.innerHTML = v;
20885                 this.cleanUpPaste();
20886                 this.el.dom.value = d.innerHTML;
20887                 this.owner.fireEvent('push', this, v);
20888             }
20889         }
20890     },
20891
20892     // private
20893     deferFocus : function(){
20894         this.focus.defer(10, this);
20895     },
20896
20897     // doc'ed in Field
20898     focus : function(){
20899         if(this.win && !this.sourceEditMode){
20900             this.win.focus();
20901         }else{
20902             this.el.focus();
20903         }
20904     },
20905     
20906     assignDocWin: function()
20907     {
20908         var iframe = this.iframe;
20909         
20910          if(Roo.isIE){
20911             this.doc = iframe.contentWindow.document;
20912             this.win = iframe.contentWindow;
20913         } else {
20914 //            if (!Roo.get(this.frameId)) {
20915 //                return;
20916 //            }
20917 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20918 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20919             
20920             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20921                 return;
20922             }
20923             
20924             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20925             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20926         }
20927     },
20928     
20929     // private
20930     initEditor : function(){
20931         //console.log("INIT EDITOR");
20932         this.assignDocWin();
20933         
20934         
20935         
20936         this.doc.designMode="on";
20937         this.doc.open();
20938         this.doc.write(this.getDocMarkup());
20939         this.doc.close();
20940         
20941         var dbody = (this.doc.body || this.doc.documentElement);
20942         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20943         // this copies styles from the containing element into thsi one..
20944         // not sure why we need all of this..
20945         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20946         
20947         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20948         //ss['background-attachment'] = 'fixed'; // w3c
20949         dbody.bgProperties = 'fixed'; // ie
20950         //Roo.DomHelper.applyStyles(dbody, ss);
20951         Roo.EventManager.on(this.doc, {
20952             //'mousedown': this.onEditorEvent,
20953             'mouseup': this.onEditorEvent,
20954             'dblclick': this.onEditorEvent,
20955             'click': this.onEditorEvent,
20956             'keyup': this.onEditorEvent,
20957             buffer:100,
20958             scope: this
20959         });
20960         if(Roo.isGecko){
20961             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20962         }
20963         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20964             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20965         }
20966         this.initialized = true;
20967
20968         this.owner.fireEvent('initialize', this);
20969         this.pushValue();
20970     },
20971
20972     // private
20973     onDestroy : function(){
20974         
20975         
20976         
20977         if(this.rendered){
20978             
20979             //for (var i =0; i < this.toolbars.length;i++) {
20980             //    // fixme - ask toolbars for heights?
20981             //    this.toolbars[i].onDestroy();
20982            // }
20983             
20984             //this.wrap.dom.innerHTML = '';
20985             //this.wrap.remove();
20986         }
20987     },
20988
20989     // private
20990     onFirstFocus : function(){
20991         
20992         this.assignDocWin();
20993         
20994         
20995         this.activated = true;
20996          
20997     
20998         if(Roo.isGecko){ // prevent silly gecko errors
20999             this.win.focus();
21000             var s = this.win.getSelection();
21001             if(!s.focusNode || s.focusNode.nodeType != 3){
21002                 var r = s.getRangeAt(0);
21003                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21004                 r.collapse(true);
21005                 this.deferFocus();
21006             }
21007             try{
21008                 this.execCmd('useCSS', true);
21009                 this.execCmd('styleWithCSS', false);
21010             }catch(e){}
21011         }
21012         this.owner.fireEvent('activate', this);
21013     },
21014
21015     // private
21016     adjustFont: function(btn){
21017         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21018         //if(Roo.isSafari){ // safari
21019         //    adjust *= 2;
21020        // }
21021         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21022         if(Roo.isSafari){ // safari
21023             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21024             v =  (v < 10) ? 10 : v;
21025             v =  (v > 48) ? 48 : v;
21026             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21027             
21028         }
21029         
21030         
21031         v = Math.max(1, v+adjust);
21032         
21033         this.execCmd('FontSize', v  );
21034     },
21035
21036     onEditorEvent : function(e)
21037     {
21038         this.owner.fireEvent('editorevent', this, e);
21039       //  this.updateToolbar();
21040         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21041     },
21042
21043     insertTag : function(tg)
21044     {
21045         // could be a bit smarter... -> wrap the current selected tRoo..
21046         if (tg.toLowerCase() == 'span' ||
21047             tg.toLowerCase() == 'code' ||
21048             tg.toLowerCase() == 'sup' ||
21049             tg.toLowerCase() == 'sub' 
21050             ) {
21051             
21052             range = this.createRange(this.getSelection());
21053             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21054             wrappingNode.appendChild(range.extractContents());
21055             range.insertNode(wrappingNode);
21056
21057             return;
21058             
21059             
21060             
21061         }
21062         this.execCmd("formatblock",   tg);
21063         
21064     },
21065     
21066     insertText : function(txt)
21067     {
21068         
21069         
21070         var range = this.createRange();
21071         range.deleteContents();
21072                //alert(Sender.getAttribute('label'));
21073                
21074         range.insertNode(this.doc.createTextNode(txt));
21075     } ,
21076     
21077      
21078
21079     /**
21080      * Executes a Midas editor command on the editor document and performs necessary focus and
21081      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21082      * @param {String} cmd The Midas command
21083      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21084      */
21085     relayCmd : function(cmd, value){
21086         this.win.focus();
21087         this.execCmd(cmd, value);
21088         this.owner.fireEvent('editorevent', this);
21089         //this.updateToolbar();
21090         this.owner.deferFocus();
21091     },
21092
21093     /**
21094      * Executes a Midas editor command directly on the editor document.
21095      * For visual commands, you should use {@link #relayCmd} instead.
21096      * <b>This should only be called after the editor is initialized.</b>
21097      * @param {String} cmd The Midas command
21098      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21099      */
21100     execCmd : function(cmd, value){
21101         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21102         this.syncValue();
21103     },
21104  
21105  
21106    
21107     /**
21108      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21109      * to insert tRoo.
21110      * @param {String} text | dom node.. 
21111      */
21112     insertAtCursor : function(text)
21113     {
21114         
21115         if(!this.activated){
21116             return;
21117         }
21118         /*
21119         if(Roo.isIE){
21120             this.win.focus();
21121             var r = this.doc.selection.createRange();
21122             if(r){
21123                 r.collapse(true);
21124                 r.pasteHTML(text);
21125                 this.syncValue();
21126                 this.deferFocus();
21127             
21128             }
21129             return;
21130         }
21131         */
21132         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21133             this.win.focus();
21134             
21135             
21136             // from jquery ui (MIT licenced)
21137             var range, node;
21138             var win = this.win;
21139             
21140             if (win.getSelection && win.getSelection().getRangeAt) {
21141                 range = win.getSelection().getRangeAt(0);
21142                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21143                 range.insertNode(node);
21144             } else if (win.document.selection && win.document.selection.createRange) {
21145                 // no firefox support
21146                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21147                 win.document.selection.createRange().pasteHTML(txt);
21148             } else {
21149                 // no firefox support
21150                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21151                 this.execCmd('InsertHTML', txt);
21152             } 
21153             
21154             this.syncValue();
21155             
21156             this.deferFocus();
21157         }
21158     },
21159  // private
21160     mozKeyPress : function(e){
21161         if(e.ctrlKey){
21162             var c = e.getCharCode(), cmd;
21163           
21164             if(c > 0){
21165                 c = String.fromCharCode(c).toLowerCase();
21166                 switch(c){
21167                     case 'b':
21168                         cmd = 'bold';
21169                         break;
21170                     case 'i':
21171                         cmd = 'italic';
21172                         break;
21173                     
21174                     case 'u':
21175                         cmd = 'underline';
21176                         break;
21177                     
21178                     case 'v':
21179                         this.cleanUpPaste.defer(100, this);
21180                         return;
21181                         
21182                 }
21183                 if(cmd){
21184                     this.win.focus();
21185                     this.execCmd(cmd);
21186                     this.deferFocus();
21187                     e.preventDefault();
21188                 }
21189                 
21190             }
21191         }
21192     },
21193
21194     // private
21195     fixKeys : function(){ // load time branching for fastest keydown performance
21196         if(Roo.isIE){
21197             return function(e){
21198                 var k = e.getKey(), r;
21199                 if(k == e.TAB){
21200                     e.stopEvent();
21201                     r = this.doc.selection.createRange();
21202                     if(r){
21203                         r.collapse(true);
21204                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21205                         this.deferFocus();
21206                     }
21207                     return;
21208                 }
21209                 
21210                 if(k == e.ENTER){
21211                     r = this.doc.selection.createRange();
21212                     if(r){
21213                         var target = r.parentElement();
21214                         if(!target || target.tagName.toLowerCase() != 'li'){
21215                             e.stopEvent();
21216                             r.pasteHTML('<br />');
21217                             r.collapse(false);
21218                             r.select();
21219                         }
21220                     }
21221                 }
21222                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21223                     this.cleanUpPaste.defer(100, this);
21224                     return;
21225                 }
21226                 
21227                 
21228             };
21229         }else if(Roo.isOpera){
21230             return function(e){
21231                 var k = e.getKey();
21232                 if(k == e.TAB){
21233                     e.stopEvent();
21234                     this.win.focus();
21235                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21236                     this.deferFocus();
21237                 }
21238                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21239                     this.cleanUpPaste.defer(100, this);
21240                     return;
21241                 }
21242                 
21243             };
21244         }else if(Roo.isSafari){
21245             return function(e){
21246                 var k = e.getKey();
21247                 
21248                 if(k == e.TAB){
21249                     e.stopEvent();
21250                     this.execCmd('InsertText','\t');
21251                     this.deferFocus();
21252                     return;
21253                 }
21254                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21255                     this.cleanUpPaste.defer(100, this);
21256                     return;
21257                 }
21258                 
21259              };
21260         }
21261     }(),
21262     
21263     getAllAncestors: function()
21264     {
21265         var p = this.getSelectedNode();
21266         var a = [];
21267         if (!p) {
21268             a.push(p); // push blank onto stack..
21269             p = this.getParentElement();
21270         }
21271         
21272         
21273         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21274             a.push(p);
21275             p = p.parentNode;
21276         }
21277         a.push(this.doc.body);
21278         return a;
21279     },
21280     lastSel : false,
21281     lastSelNode : false,
21282     
21283     
21284     getSelection : function() 
21285     {
21286         this.assignDocWin();
21287         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21288     },
21289     
21290     getSelectedNode: function() 
21291     {
21292         // this may only work on Gecko!!!
21293         
21294         // should we cache this!!!!
21295         
21296         
21297         
21298          
21299         var range = this.createRange(this.getSelection()).cloneRange();
21300         
21301         if (Roo.isIE) {
21302             var parent = range.parentElement();
21303             while (true) {
21304                 var testRange = range.duplicate();
21305                 testRange.moveToElementText(parent);
21306                 if (testRange.inRange(range)) {
21307                     break;
21308                 }
21309                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21310                     break;
21311                 }
21312                 parent = parent.parentElement;
21313             }
21314             return parent;
21315         }
21316         
21317         // is ancestor a text element.
21318         var ac =  range.commonAncestorContainer;
21319         if (ac.nodeType == 3) {
21320             ac = ac.parentNode;
21321         }
21322         
21323         var ar = ac.childNodes;
21324          
21325         var nodes = [];
21326         var other_nodes = [];
21327         var has_other_nodes = false;
21328         for (var i=0;i<ar.length;i++) {
21329             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21330                 continue;
21331             }
21332             // fullly contained node.
21333             
21334             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21335                 nodes.push(ar[i]);
21336                 continue;
21337             }
21338             
21339             // probably selected..
21340             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21341                 other_nodes.push(ar[i]);
21342                 continue;
21343             }
21344             // outer..
21345             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21346                 continue;
21347             }
21348             
21349             
21350             has_other_nodes = true;
21351         }
21352         if (!nodes.length && other_nodes.length) {
21353             nodes= other_nodes;
21354         }
21355         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21356             return false;
21357         }
21358         
21359         return nodes[0];
21360     },
21361     createRange: function(sel)
21362     {
21363         // this has strange effects when using with 
21364         // top toolbar - not sure if it's a great idea.
21365         //this.editor.contentWindow.focus();
21366         if (typeof sel != "undefined") {
21367             try {
21368                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21369             } catch(e) {
21370                 return this.doc.createRange();
21371             }
21372         } else {
21373             return this.doc.createRange();
21374         }
21375     },
21376     getParentElement: function()
21377     {
21378         
21379         this.assignDocWin();
21380         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21381         
21382         var range = this.createRange(sel);
21383          
21384         try {
21385             var p = range.commonAncestorContainer;
21386             while (p.nodeType == 3) { // text node
21387                 p = p.parentNode;
21388             }
21389             return p;
21390         } catch (e) {
21391             return null;
21392         }
21393     
21394     },
21395     /***
21396      *
21397      * Range intersection.. the hard stuff...
21398      *  '-1' = before
21399      *  '0' = hits..
21400      *  '1' = after.
21401      *         [ -- selected range --- ]
21402      *   [fail]                        [fail]
21403      *
21404      *    basically..
21405      *      if end is before start or  hits it. fail.
21406      *      if start is after end or hits it fail.
21407      *
21408      *   if either hits (but other is outside. - then it's not 
21409      *   
21410      *    
21411      **/
21412     
21413     
21414     // @see http://www.thismuchiknow.co.uk/?p=64.
21415     rangeIntersectsNode : function(range, node)
21416     {
21417         var nodeRange = node.ownerDocument.createRange();
21418         try {
21419             nodeRange.selectNode(node);
21420         } catch (e) {
21421             nodeRange.selectNodeContents(node);
21422         }
21423     
21424         var rangeStartRange = range.cloneRange();
21425         rangeStartRange.collapse(true);
21426     
21427         var rangeEndRange = range.cloneRange();
21428         rangeEndRange.collapse(false);
21429     
21430         var nodeStartRange = nodeRange.cloneRange();
21431         nodeStartRange.collapse(true);
21432     
21433         var nodeEndRange = nodeRange.cloneRange();
21434         nodeEndRange.collapse(false);
21435     
21436         return rangeStartRange.compareBoundaryPoints(
21437                  Range.START_TO_START, nodeEndRange) == -1 &&
21438                rangeEndRange.compareBoundaryPoints(
21439                  Range.START_TO_START, nodeStartRange) == 1;
21440         
21441          
21442     },
21443     rangeCompareNode : function(range, node)
21444     {
21445         var nodeRange = node.ownerDocument.createRange();
21446         try {
21447             nodeRange.selectNode(node);
21448         } catch (e) {
21449             nodeRange.selectNodeContents(node);
21450         }
21451         
21452         
21453         range.collapse(true);
21454     
21455         nodeRange.collapse(true);
21456      
21457         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21458         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21459          
21460         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21461         
21462         var nodeIsBefore   =  ss == 1;
21463         var nodeIsAfter    = ee == -1;
21464         
21465         if (nodeIsBefore && nodeIsAfter) {
21466             return 0; // outer
21467         }
21468         if (!nodeIsBefore && nodeIsAfter) {
21469             return 1; //right trailed.
21470         }
21471         
21472         if (nodeIsBefore && !nodeIsAfter) {
21473             return 2;  // left trailed.
21474         }
21475         // fully contined.
21476         return 3;
21477     },
21478
21479     // private? - in a new class?
21480     cleanUpPaste :  function()
21481     {
21482         // cleans up the whole document..
21483         Roo.log('cleanuppaste');
21484         
21485         this.cleanUpChildren(this.doc.body);
21486         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21487         if (clean != this.doc.body.innerHTML) {
21488             this.doc.body.innerHTML = clean;
21489         }
21490         
21491     },
21492     
21493     cleanWordChars : function(input) {// change the chars to hex code
21494         var he = Roo.HtmlEditorCore;
21495         
21496         var output = input;
21497         Roo.each(he.swapCodes, function(sw) { 
21498             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21499             
21500             output = output.replace(swapper, sw[1]);
21501         });
21502         
21503         return output;
21504     },
21505     
21506     
21507     cleanUpChildren : function (n)
21508     {
21509         if (!n.childNodes.length) {
21510             return;
21511         }
21512         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21513            this.cleanUpChild(n.childNodes[i]);
21514         }
21515     },
21516     
21517     
21518         
21519     
21520     cleanUpChild : function (node)
21521     {
21522         var ed = this;
21523         //console.log(node);
21524         if (node.nodeName == "#text") {
21525             // clean up silly Windows -- stuff?
21526             return; 
21527         }
21528         if (node.nodeName == "#comment") {
21529             if (!this.allowComments) {
21530                 node.parentNode.removeChild(node);
21531             }
21532             // clean up silly Windows -- stuff?
21533             return; 
21534         }
21535         var lcname = node.tagName.toLowerCase();
21536         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21537         // whitelist of tags..
21538         
21539         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21540             // remove node.
21541             node.parentNode.removeChild(node);
21542             return;
21543             
21544         }
21545         
21546         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21547         
21548         // spans with no attributes - just remove them..
21549         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21550             remove_keep_children = true;
21551         }
21552         
21553         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21554         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21555         
21556         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21557         //    remove_keep_children = true;
21558         //}
21559         
21560         if (remove_keep_children) {
21561             this.cleanUpChildren(node);
21562             // inserts everything just before this node...
21563             while (node.childNodes.length) {
21564                 var cn = node.childNodes[0];
21565                 node.removeChild(cn);
21566                 node.parentNode.insertBefore(cn, node);
21567             }
21568             node.parentNode.removeChild(node);
21569             return;
21570         }
21571         
21572         if (!node.attributes || !node.attributes.length) {
21573             
21574           
21575             
21576             
21577             this.cleanUpChildren(node);
21578             return;
21579         }
21580         
21581         function cleanAttr(n,v)
21582         {
21583             
21584             if (v.match(/^\./) || v.match(/^\//)) {
21585                 return;
21586             }
21587             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21588                 return;
21589             }
21590             if (v.match(/^#/)) {
21591                 return;
21592             }
21593             if (v.match(/^\{/)) { // allow template editing.
21594                 return;
21595             }
21596 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21597             node.removeAttribute(n);
21598             
21599         }
21600         
21601         var cwhite = this.cwhite;
21602         var cblack = this.cblack;
21603             
21604         function cleanStyle(n,v)
21605         {
21606             if (v.match(/expression/)) { //XSS?? should we even bother..
21607                 node.removeAttribute(n);
21608                 return;
21609             }
21610             
21611             var parts = v.split(/;/);
21612             var clean = [];
21613             
21614             Roo.each(parts, function(p) {
21615                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21616                 if (!p.length) {
21617                     return true;
21618                 }
21619                 var l = p.split(':').shift().replace(/\s+/g,'');
21620                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21621                 
21622                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21623 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21624                     //node.removeAttribute(n);
21625                     return true;
21626                 }
21627                 //Roo.log()
21628                 // only allow 'c whitelisted system attributes'
21629                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21630 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21631                     //node.removeAttribute(n);
21632                     return true;
21633                 }
21634                 
21635                 
21636                  
21637                 
21638                 clean.push(p);
21639                 return true;
21640             });
21641             if (clean.length) { 
21642                 node.setAttribute(n, clean.join(';'));
21643             } else {
21644                 node.removeAttribute(n);
21645             }
21646             
21647         }
21648         
21649         
21650         for (var i = node.attributes.length-1; i > -1 ; i--) {
21651             var a = node.attributes[i];
21652             //console.log(a);
21653             
21654             if (a.name.toLowerCase().substr(0,2)=='on')  {
21655                 node.removeAttribute(a.name);
21656                 continue;
21657             }
21658             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21659                 node.removeAttribute(a.name);
21660                 continue;
21661             }
21662             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21663                 cleanAttr(a.name,a.value); // fixme..
21664                 continue;
21665             }
21666             if (a.name == 'style') {
21667                 cleanStyle(a.name,a.value);
21668                 continue;
21669             }
21670             /// clean up MS crap..
21671             // tecnically this should be a list of valid class'es..
21672             
21673             
21674             if (a.name == 'class') {
21675                 if (a.value.match(/^Mso/)) {
21676                     node.removeAttribute('class');
21677                 }
21678                 
21679                 if (a.value.match(/^body$/)) {
21680                     node.removeAttribute('class');
21681                 }
21682                 continue;
21683             }
21684             
21685             // style cleanup!?
21686             // class cleanup?
21687             
21688         }
21689         
21690         
21691         this.cleanUpChildren(node);
21692         
21693         
21694     },
21695     
21696     /**
21697      * Clean up MS wordisms...
21698      */
21699     cleanWord : function(node)
21700     {
21701         if (!node) {
21702             this.cleanWord(this.doc.body);
21703             return;
21704         }
21705         
21706         if(
21707                 node.nodeName == 'SPAN' &&
21708                 !node.hasAttributes() &&
21709                 node.childNodes.length == 1 &&
21710                 node.firstChild.nodeName == "#text"  
21711         ) {
21712             var textNode = node.firstChild;
21713             node.removeChild(textNode);
21714             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21715                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21716             }
21717             node.parentNode.insertBefore(textNode, node);
21718             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21719                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21720             }
21721             node.parentNode.removeChild(node);
21722         }
21723         
21724         if (node.nodeName == "#text") {
21725             // clean up silly Windows -- stuff?
21726             return; 
21727         }
21728         if (node.nodeName == "#comment") {
21729             node.parentNode.removeChild(node);
21730             // clean up silly Windows -- stuff?
21731             return; 
21732         }
21733         
21734         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21735             node.parentNode.removeChild(node);
21736             return;
21737         }
21738         //Roo.log(node.tagName);
21739         // remove - but keep children..
21740         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21741             //Roo.log('-- removed');
21742             while (node.childNodes.length) {
21743                 var cn = node.childNodes[0];
21744                 node.removeChild(cn);
21745                 node.parentNode.insertBefore(cn, node);
21746                 // move node to parent - and clean it..
21747                 this.cleanWord(cn);
21748             }
21749             node.parentNode.removeChild(node);
21750             /// no need to iterate chidlren = it's got none..
21751             //this.iterateChildren(node, this.cleanWord);
21752             return;
21753         }
21754         // clean styles
21755         if (node.className.length) {
21756             
21757             var cn = node.className.split(/\W+/);
21758             var cna = [];
21759             Roo.each(cn, function(cls) {
21760                 if (cls.match(/Mso[a-zA-Z]+/)) {
21761                     return;
21762                 }
21763                 cna.push(cls);
21764             });
21765             node.className = cna.length ? cna.join(' ') : '';
21766             if (!cna.length) {
21767                 node.removeAttribute("class");
21768             }
21769         }
21770         
21771         if (node.hasAttribute("lang")) {
21772             node.removeAttribute("lang");
21773         }
21774         
21775         if (node.hasAttribute("style")) {
21776             
21777             var styles = node.getAttribute("style").split(";");
21778             var nstyle = [];
21779             Roo.each(styles, function(s) {
21780                 if (!s.match(/:/)) {
21781                     return;
21782                 }
21783                 var kv = s.split(":");
21784                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21785                     return;
21786                 }
21787                 // what ever is left... we allow.
21788                 nstyle.push(s);
21789             });
21790             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21791             if (!nstyle.length) {
21792                 node.removeAttribute('style');
21793             }
21794         }
21795         this.iterateChildren(node, this.cleanWord);
21796         
21797         
21798         
21799     },
21800     /**
21801      * iterateChildren of a Node, calling fn each time, using this as the scole..
21802      * @param {DomNode} node node to iterate children of.
21803      * @param {Function} fn method of this class to call on each item.
21804      */
21805     iterateChildren : function(node, fn)
21806     {
21807         if (!node.childNodes.length) {
21808                 return;
21809         }
21810         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21811            fn.call(this, node.childNodes[i])
21812         }
21813     },
21814     
21815     
21816     /**
21817      * cleanTableWidths.
21818      *
21819      * Quite often pasting from word etc.. results in tables with column and widths.
21820      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21821      *
21822      */
21823     cleanTableWidths : function(node)
21824     {
21825          
21826          
21827         if (!node) {
21828             this.cleanTableWidths(this.doc.body);
21829             return;
21830         }
21831         
21832         // ignore list...
21833         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21834             return; 
21835         }
21836         Roo.log(node.tagName);
21837         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21838             this.iterateChildren(node, this.cleanTableWidths);
21839             return;
21840         }
21841         if (node.hasAttribute('width')) {
21842             node.removeAttribute('width');
21843         }
21844         
21845          
21846         if (node.hasAttribute("style")) {
21847             // pretty basic...
21848             
21849             var styles = node.getAttribute("style").split(";");
21850             var nstyle = [];
21851             Roo.each(styles, function(s) {
21852                 if (!s.match(/:/)) {
21853                     return;
21854                 }
21855                 var kv = s.split(":");
21856                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21857                     return;
21858                 }
21859                 // what ever is left... we allow.
21860                 nstyle.push(s);
21861             });
21862             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21863             if (!nstyle.length) {
21864                 node.removeAttribute('style');
21865             }
21866         }
21867         
21868         this.iterateChildren(node, this.cleanTableWidths);
21869         
21870         
21871     },
21872     
21873     
21874     
21875     
21876     domToHTML : function(currentElement, depth, nopadtext) {
21877         
21878         depth = depth || 0;
21879         nopadtext = nopadtext || false;
21880     
21881         if (!currentElement) {
21882             return this.domToHTML(this.doc.body);
21883         }
21884         
21885         //Roo.log(currentElement);
21886         var j;
21887         var allText = false;
21888         var nodeName = currentElement.nodeName;
21889         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21890         
21891         if  (nodeName == '#text') {
21892             
21893             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21894         }
21895         
21896         
21897         var ret = '';
21898         if (nodeName != 'BODY') {
21899              
21900             var i = 0;
21901             // Prints the node tagName, such as <A>, <IMG>, etc
21902             if (tagName) {
21903                 var attr = [];
21904                 for(i = 0; i < currentElement.attributes.length;i++) {
21905                     // quoting?
21906                     var aname = currentElement.attributes.item(i).name;
21907                     if (!currentElement.attributes.item(i).value.length) {
21908                         continue;
21909                     }
21910                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21911                 }
21912                 
21913                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21914             } 
21915             else {
21916                 
21917                 // eack
21918             }
21919         } else {
21920             tagName = false;
21921         }
21922         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21923             return ret;
21924         }
21925         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21926             nopadtext = true;
21927         }
21928         
21929         
21930         // Traverse the tree
21931         i = 0;
21932         var currentElementChild = currentElement.childNodes.item(i);
21933         var allText = true;
21934         var innerHTML  = '';
21935         lastnode = '';
21936         while (currentElementChild) {
21937             // Formatting code (indent the tree so it looks nice on the screen)
21938             var nopad = nopadtext;
21939             if (lastnode == 'SPAN') {
21940                 nopad  = true;
21941             }
21942             // text
21943             if  (currentElementChild.nodeName == '#text') {
21944                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21945                 toadd = nopadtext ? toadd : toadd.trim();
21946                 if (!nopad && toadd.length > 80) {
21947                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21948                 }
21949                 innerHTML  += toadd;
21950                 
21951                 i++;
21952                 currentElementChild = currentElement.childNodes.item(i);
21953                 lastNode = '';
21954                 continue;
21955             }
21956             allText = false;
21957             
21958             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21959                 
21960             // Recursively traverse the tree structure of the child node
21961             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21962             lastnode = currentElementChild.nodeName;
21963             i++;
21964             currentElementChild=currentElement.childNodes.item(i);
21965         }
21966         
21967         ret += innerHTML;
21968         
21969         if (!allText) {
21970                 // The remaining code is mostly for formatting the tree
21971             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21972         }
21973         
21974         
21975         if (tagName) {
21976             ret+= "</"+tagName+">";
21977         }
21978         return ret;
21979         
21980     },
21981         
21982     applyBlacklists : function()
21983     {
21984         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21985         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21986         
21987         this.white = [];
21988         this.black = [];
21989         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21990             if (b.indexOf(tag) > -1) {
21991                 return;
21992             }
21993             this.white.push(tag);
21994             
21995         }, this);
21996         
21997         Roo.each(w, function(tag) {
21998             if (b.indexOf(tag) > -1) {
21999                 return;
22000             }
22001             if (this.white.indexOf(tag) > -1) {
22002                 return;
22003             }
22004             this.white.push(tag);
22005             
22006         }, this);
22007         
22008         
22009         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22010             if (w.indexOf(tag) > -1) {
22011                 return;
22012             }
22013             this.black.push(tag);
22014             
22015         }, this);
22016         
22017         Roo.each(b, function(tag) {
22018             if (w.indexOf(tag) > -1) {
22019                 return;
22020             }
22021             if (this.black.indexOf(tag) > -1) {
22022                 return;
22023             }
22024             this.black.push(tag);
22025             
22026         }, this);
22027         
22028         
22029         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22030         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22031         
22032         this.cwhite = [];
22033         this.cblack = [];
22034         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22035             if (b.indexOf(tag) > -1) {
22036                 return;
22037             }
22038             this.cwhite.push(tag);
22039             
22040         }, this);
22041         
22042         Roo.each(w, function(tag) {
22043             if (b.indexOf(tag) > -1) {
22044                 return;
22045             }
22046             if (this.cwhite.indexOf(tag) > -1) {
22047                 return;
22048             }
22049             this.cwhite.push(tag);
22050             
22051         }, this);
22052         
22053         
22054         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22055             if (w.indexOf(tag) > -1) {
22056                 return;
22057             }
22058             this.cblack.push(tag);
22059             
22060         }, this);
22061         
22062         Roo.each(b, function(tag) {
22063             if (w.indexOf(tag) > -1) {
22064                 return;
22065             }
22066             if (this.cblack.indexOf(tag) > -1) {
22067                 return;
22068             }
22069             this.cblack.push(tag);
22070             
22071         }, this);
22072     },
22073     
22074     setStylesheets : function(stylesheets)
22075     {
22076         if(typeof(stylesheets) == 'string'){
22077             Roo.get(this.iframe.contentDocument.head).createChild({
22078                 tag : 'link',
22079                 rel : 'stylesheet',
22080                 type : 'text/css',
22081                 href : stylesheets
22082             });
22083             
22084             return;
22085         }
22086         var _this = this;
22087      
22088         Roo.each(stylesheets, function(s) {
22089             if(!s.length){
22090                 return;
22091             }
22092             
22093             Roo.get(_this.iframe.contentDocument.head).createChild({
22094                 tag : 'link',
22095                 rel : 'stylesheet',
22096                 type : 'text/css',
22097                 href : s
22098             });
22099         });
22100
22101         
22102     },
22103     
22104     removeStylesheets : function()
22105     {
22106         var _this = this;
22107         
22108         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22109             s.remove();
22110         });
22111     },
22112     
22113     setStyle : function(style)
22114     {
22115         Roo.get(this.iframe.contentDocument.head).createChild({
22116             tag : 'style',
22117             type : 'text/css',
22118             html : style
22119         });
22120
22121         return;
22122     }
22123     
22124     // hide stuff that is not compatible
22125     /**
22126      * @event blur
22127      * @hide
22128      */
22129     /**
22130      * @event change
22131      * @hide
22132      */
22133     /**
22134      * @event focus
22135      * @hide
22136      */
22137     /**
22138      * @event specialkey
22139      * @hide
22140      */
22141     /**
22142      * @cfg {String} fieldClass @hide
22143      */
22144     /**
22145      * @cfg {String} focusClass @hide
22146      */
22147     /**
22148      * @cfg {String} autoCreate @hide
22149      */
22150     /**
22151      * @cfg {String} inputType @hide
22152      */
22153     /**
22154      * @cfg {String} invalidClass @hide
22155      */
22156     /**
22157      * @cfg {String} invalidText @hide
22158      */
22159     /**
22160      * @cfg {String} msgFx @hide
22161      */
22162     /**
22163      * @cfg {String} validateOnBlur @hide
22164      */
22165 });
22166
22167 Roo.HtmlEditorCore.white = [
22168         'area', 'br', 'img', 'input', 'hr', 'wbr',
22169         
22170        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22171        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22172        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22173        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22174        'table',   'ul',         'xmp', 
22175        
22176        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22177       'thead',   'tr', 
22178      
22179       'dir', 'menu', 'ol', 'ul', 'dl',
22180        
22181       'embed',  'object'
22182 ];
22183
22184
22185 Roo.HtmlEditorCore.black = [
22186     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22187         'applet', // 
22188         'base',   'basefont', 'bgsound', 'blink',  'body', 
22189         'frame',  'frameset', 'head',    'html',   'ilayer', 
22190         'iframe', 'layer',  'link',     'meta',    'object',   
22191         'script', 'style' ,'title',  'xml' // clean later..
22192 ];
22193 Roo.HtmlEditorCore.clean = [
22194     'script', 'style', 'title', 'xml'
22195 ];
22196 Roo.HtmlEditorCore.remove = [
22197     'font'
22198 ];
22199 // attributes..
22200
22201 Roo.HtmlEditorCore.ablack = [
22202     'on'
22203 ];
22204     
22205 Roo.HtmlEditorCore.aclean = [ 
22206     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22207 ];
22208
22209 // protocols..
22210 Roo.HtmlEditorCore.pwhite= [
22211         'http',  'https',  'mailto'
22212 ];
22213
22214 // white listed style attributes.
22215 Roo.HtmlEditorCore.cwhite= [
22216       //  'text-align', /// default is to allow most things..
22217       
22218          
22219 //        'font-size'//??
22220 ];
22221
22222 // black listed style attributes.
22223 Roo.HtmlEditorCore.cblack= [
22224       //  'font-size' -- this can be set by the project 
22225 ];
22226
22227
22228 Roo.HtmlEditorCore.swapCodes   =[ 
22229     [    8211, "&#8211;" ], 
22230     [    8212, "&#8212;" ], 
22231     [    8216,  "'" ],  
22232     [    8217, "'" ],  
22233     [    8220, '"' ],  
22234     [    8221, '"' ],  
22235     [    8226, "*" ],  
22236     [    8230, "..." ]
22237 ]; 
22238
22239     //<script type="text/javascript">
22240
22241 /*
22242  * Ext JS Library 1.1.1
22243  * Copyright(c) 2006-2007, Ext JS, LLC.
22244  * Licence LGPL
22245  * 
22246  */
22247  
22248  
22249 Roo.form.HtmlEditor = function(config){
22250     
22251     
22252     
22253     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22254     
22255     if (!this.toolbars) {
22256         this.toolbars = [];
22257     }
22258     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22259     
22260     
22261 };
22262
22263 /**
22264  * @class Roo.form.HtmlEditor
22265  * @extends Roo.form.Field
22266  * Provides a lightweight HTML Editor component.
22267  *
22268  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22269  * 
22270  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22271  * supported by this editor.</b><br/><br/>
22272  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22273  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22274  */
22275 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22276     /**
22277      * @cfg {Boolean} clearUp
22278      */
22279     clearUp : true,
22280       /**
22281      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22282      */
22283     toolbars : false,
22284    
22285      /**
22286      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22287      *                        Roo.resizable.
22288      */
22289     resizable : false,
22290      /**
22291      * @cfg {Number} height (in pixels)
22292      */   
22293     height: 300,
22294    /**
22295      * @cfg {Number} width (in pixels)
22296      */   
22297     width: 500,
22298     
22299     /**
22300      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22301      * 
22302      */
22303     stylesheets: false,
22304     
22305     
22306      /**
22307      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22308      * 
22309      */
22310     cblack: false,
22311     /**
22312      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22313      * 
22314      */
22315     cwhite: false,
22316     
22317      /**
22318      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22319      * 
22320      */
22321     black: false,
22322     /**
22323      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22324      * 
22325      */
22326     white: false,
22327     /**
22328      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22329      */
22330     allowComments: false,
22331     
22332     // id of frame..
22333     frameId: false,
22334     
22335     // private properties
22336     validationEvent : false,
22337     deferHeight: true,
22338     initialized : false,
22339     activated : false,
22340     
22341     onFocus : Roo.emptyFn,
22342     iframePad:3,
22343     hideMode:'offsets',
22344     
22345     actionMode : 'container', // defaults to hiding it...
22346     
22347     defaultAutoCreate : { // modified by initCompnoent..
22348         tag: "textarea",
22349         style:"width:500px;height:300px;",
22350         autocomplete: "new-password"
22351     },
22352
22353     // private
22354     initComponent : function(){
22355         this.addEvents({
22356             /**
22357              * @event initialize
22358              * Fires when the editor is fully initialized (including the iframe)
22359              * @param {HtmlEditor} this
22360              */
22361             initialize: true,
22362             /**
22363              * @event activate
22364              * Fires when the editor is first receives the focus. Any insertion must wait
22365              * until after this event.
22366              * @param {HtmlEditor} this
22367              */
22368             activate: true,
22369              /**
22370              * @event beforesync
22371              * Fires before the textarea is updated with content from the editor iframe. Return false
22372              * to cancel the sync.
22373              * @param {HtmlEditor} this
22374              * @param {String} html
22375              */
22376             beforesync: true,
22377              /**
22378              * @event beforepush
22379              * Fires before the iframe editor is updated with content from the textarea. Return false
22380              * to cancel the push.
22381              * @param {HtmlEditor} this
22382              * @param {String} html
22383              */
22384             beforepush: true,
22385              /**
22386              * @event sync
22387              * Fires when the textarea is updated with content from the editor iframe.
22388              * @param {HtmlEditor} this
22389              * @param {String} html
22390              */
22391             sync: true,
22392              /**
22393              * @event push
22394              * Fires when the iframe editor is updated with content from the textarea.
22395              * @param {HtmlEditor} this
22396              * @param {String} html
22397              */
22398             push: true,
22399              /**
22400              * @event editmodechange
22401              * Fires when the editor switches edit modes
22402              * @param {HtmlEditor} this
22403              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22404              */
22405             editmodechange: true,
22406             /**
22407              * @event editorevent
22408              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22409              * @param {HtmlEditor} this
22410              */
22411             editorevent: true,
22412             /**
22413              * @event firstfocus
22414              * Fires when on first focus - needed by toolbars..
22415              * @param {HtmlEditor} this
22416              */
22417             firstfocus: true,
22418             /**
22419              * @event autosave
22420              * Auto save the htmlEditor value as a file into Events
22421              * @param {HtmlEditor} this
22422              */
22423             autosave: true,
22424             /**
22425              * @event savedpreview
22426              * preview the saved version of htmlEditor
22427              * @param {HtmlEditor} this
22428              */
22429             savedpreview: true,
22430             
22431             /**
22432             * @event stylesheetsclick
22433             * Fires when press the Sytlesheets button
22434             * @param {Roo.HtmlEditorCore} this
22435             */
22436             stylesheetsclick: true
22437         });
22438         this.defaultAutoCreate =  {
22439             tag: "textarea",
22440             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22441             autocomplete: "new-password"
22442         };
22443     },
22444
22445     /**
22446      * Protected method that will not generally be called directly. It
22447      * is called when the editor creates its toolbar. Override this method if you need to
22448      * add custom toolbar buttons.
22449      * @param {HtmlEditor} editor
22450      */
22451     createToolbar : function(editor){
22452         Roo.log("create toolbars");
22453         if (!editor.toolbars || !editor.toolbars.length) {
22454             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22455         }
22456         
22457         for (var i =0 ; i < editor.toolbars.length;i++) {
22458             editor.toolbars[i] = Roo.factory(
22459                     typeof(editor.toolbars[i]) == 'string' ?
22460                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22461                 Roo.form.HtmlEditor);
22462             editor.toolbars[i].init(editor);
22463         }
22464          
22465         
22466     },
22467
22468      
22469     // private
22470     onRender : function(ct, position)
22471     {
22472         var _t = this;
22473         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22474         
22475         this.wrap = this.el.wrap({
22476             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22477         });
22478         
22479         this.editorcore.onRender(ct, position);
22480          
22481         if (this.resizable) {
22482             this.resizeEl = new Roo.Resizable(this.wrap, {
22483                 pinned : true,
22484                 wrap: true,
22485                 dynamic : true,
22486                 minHeight : this.height,
22487                 height: this.height,
22488                 handles : this.resizable,
22489                 width: this.width,
22490                 listeners : {
22491                     resize : function(r, w, h) {
22492                         _t.onResize(w,h); // -something
22493                     }
22494                 }
22495             });
22496             
22497         }
22498         this.createToolbar(this);
22499        
22500         
22501         if(!this.width){
22502             this.setSize(this.wrap.getSize());
22503         }
22504         if (this.resizeEl) {
22505             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22506             // should trigger onReize..
22507         }
22508         
22509         this.keyNav = new Roo.KeyNav(this.el, {
22510             
22511             "tab" : function(e){
22512                 e.preventDefault();
22513                 
22514                 var value = this.getValue();
22515                 
22516                 var start = this.el.dom.selectionStart;
22517                 var end = this.el.dom.selectionEnd;
22518                 
22519                 if(!e.shiftKey){
22520                     
22521                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22522                     this.el.dom.setSelectionRange(end + 1, end + 1);
22523                     return;
22524                 }
22525                 
22526                 var f = value.substring(0, start).split("\t");
22527                 
22528                 if(f.pop().length != 0){
22529                     return;
22530                 }
22531                 
22532                 this.setValue(f.join("\t") + value.substring(end));
22533                 this.el.dom.setSelectionRange(start - 1, start - 1);
22534                 
22535             },
22536             
22537             "home" : function(e){
22538                 e.preventDefault();
22539                 
22540                 var curr = this.el.dom.selectionStart;
22541                 var lines = this.getValue().split("\n");
22542                 
22543                 if(!lines.length){
22544                     return;
22545                 }
22546                 
22547                 if(e.ctrlKey){
22548                     this.el.dom.setSelectionRange(0, 0);
22549                     return;
22550                 }
22551                 
22552                 var pos = 0;
22553                 
22554                 for (var i = 0; i < lines.length;i++) {
22555                     pos += lines[i].length;
22556                     
22557                     if(i != 0){
22558                         pos += 1;
22559                     }
22560                     
22561                     if(pos < curr){
22562                         continue;
22563                     }
22564                     
22565                     pos -= lines[i].length;
22566                     
22567                     break;
22568                 }
22569                 
22570                 if(!e.shiftKey){
22571                     this.el.dom.setSelectionRange(pos, pos);
22572                     return;
22573                 }
22574                 
22575                 this.el.dom.selectionStart = pos;
22576                 this.el.dom.selectionEnd = curr;
22577             },
22578             
22579             "end" : function(e){
22580                 e.preventDefault();
22581                 
22582                 var curr = this.el.dom.selectionStart;
22583                 var lines = this.getValue().split("\n");
22584                 
22585                 if(!lines.length){
22586                     return;
22587                 }
22588                 
22589                 if(e.ctrlKey){
22590                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22591                     return;
22592                 }
22593                 
22594                 var pos = 0;
22595                 
22596                 for (var i = 0; i < lines.length;i++) {
22597                     
22598                     pos += lines[i].length;
22599                     
22600                     if(i != 0){
22601                         pos += 1;
22602                     }
22603                     
22604                     if(pos < curr){
22605                         continue;
22606                     }
22607                     
22608                     break;
22609                 }
22610                 
22611                 if(!e.shiftKey){
22612                     this.el.dom.setSelectionRange(pos, pos);
22613                     return;
22614                 }
22615                 
22616                 this.el.dom.selectionStart = curr;
22617                 this.el.dom.selectionEnd = pos;
22618             },
22619
22620             scope : this,
22621
22622             doRelay : function(foo, bar, hname){
22623                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22624             },
22625
22626             forceKeyDown: true
22627         });
22628         
22629 //        if(this.autosave && this.w){
22630 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22631 //        }
22632     },
22633
22634     // private
22635     onResize : function(w, h)
22636     {
22637         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22638         var ew = false;
22639         var eh = false;
22640         
22641         if(this.el ){
22642             if(typeof w == 'number'){
22643                 var aw = w - this.wrap.getFrameWidth('lr');
22644                 this.el.setWidth(this.adjustWidth('textarea', aw));
22645                 ew = aw;
22646             }
22647             if(typeof h == 'number'){
22648                 var tbh = 0;
22649                 for (var i =0; i < this.toolbars.length;i++) {
22650                     // fixme - ask toolbars for heights?
22651                     tbh += this.toolbars[i].tb.el.getHeight();
22652                     if (this.toolbars[i].footer) {
22653                         tbh += this.toolbars[i].footer.el.getHeight();
22654                     }
22655                 }
22656                 
22657                 
22658                 
22659                 
22660                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22661                 ah -= 5; // knock a few pixes off for look..
22662 //                Roo.log(ah);
22663                 this.el.setHeight(this.adjustWidth('textarea', ah));
22664                 var eh = ah;
22665             }
22666         }
22667         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22668         this.editorcore.onResize(ew,eh);
22669         
22670     },
22671
22672     /**
22673      * Toggles the editor between standard and source edit mode.
22674      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22675      */
22676     toggleSourceEdit : function(sourceEditMode)
22677     {
22678         this.editorcore.toggleSourceEdit(sourceEditMode);
22679         
22680         if(this.editorcore.sourceEditMode){
22681             Roo.log('editor - showing textarea');
22682             
22683 //            Roo.log('in');
22684 //            Roo.log(this.syncValue());
22685             this.editorcore.syncValue();
22686             this.el.removeClass('x-hidden');
22687             this.el.dom.removeAttribute('tabIndex');
22688             this.el.focus();
22689             
22690             for (var i = 0; i < this.toolbars.length; i++) {
22691                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22692                     this.toolbars[i].tb.hide();
22693                     this.toolbars[i].footer.hide();
22694                 }
22695             }
22696             
22697         }else{
22698             Roo.log('editor - hiding textarea');
22699 //            Roo.log('out')
22700 //            Roo.log(this.pushValue()); 
22701             this.editorcore.pushValue();
22702             
22703             this.el.addClass('x-hidden');
22704             this.el.dom.setAttribute('tabIndex', -1);
22705             
22706             for (var i = 0; i < this.toolbars.length; i++) {
22707                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22708                     this.toolbars[i].tb.show();
22709                     this.toolbars[i].footer.show();
22710                 }
22711             }
22712             
22713             //this.deferFocus();
22714         }
22715         
22716         this.setSize(this.wrap.getSize());
22717         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22718         
22719         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22720     },
22721  
22722     // private (for BoxComponent)
22723     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22724
22725     // private (for BoxComponent)
22726     getResizeEl : function(){
22727         return this.wrap;
22728     },
22729
22730     // private (for BoxComponent)
22731     getPositionEl : function(){
22732         return this.wrap;
22733     },
22734
22735     // private
22736     initEvents : function(){
22737         this.originalValue = this.getValue();
22738     },
22739
22740     /**
22741      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22742      * @method
22743      */
22744     markInvalid : Roo.emptyFn,
22745     /**
22746      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22747      * @method
22748      */
22749     clearInvalid : Roo.emptyFn,
22750
22751     setValue : function(v){
22752         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22753         this.editorcore.pushValue();
22754     },
22755
22756      
22757     // private
22758     deferFocus : function(){
22759         this.focus.defer(10, this);
22760     },
22761
22762     // doc'ed in Field
22763     focus : function(){
22764         this.editorcore.focus();
22765         
22766     },
22767       
22768
22769     // private
22770     onDestroy : function(){
22771         
22772         
22773         
22774         if(this.rendered){
22775             
22776             for (var i =0; i < this.toolbars.length;i++) {
22777                 // fixme - ask toolbars for heights?
22778                 this.toolbars[i].onDestroy();
22779             }
22780             
22781             this.wrap.dom.innerHTML = '';
22782             this.wrap.remove();
22783         }
22784     },
22785
22786     // private
22787     onFirstFocus : function(){
22788         //Roo.log("onFirstFocus");
22789         this.editorcore.onFirstFocus();
22790          for (var i =0; i < this.toolbars.length;i++) {
22791             this.toolbars[i].onFirstFocus();
22792         }
22793         
22794     },
22795     
22796     // private
22797     syncValue : function()
22798     {
22799         this.editorcore.syncValue();
22800     },
22801     
22802     pushValue : function()
22803     {
22804         this.editorcore.pushValue();
22805     },
22806     
22807     setStylesheets : function(stylesheets)
22808     {
22809         this.editorcore.setStylesheets(stylesheets);
22810     },
22811     
22812     removeStylesheets : function()
22813     {
22814         this.editorcore.removeStylesheets();
22815     }
22816      
22817     
22818     // hide stuff that is not compatible
22819     /**
22820      * @event blur
22821      * @hide
22822      */
22823     /**
22824      * @event change
22825      * @hide
22826      */
22827     /**
22828      * @event focus
22829      * @hide
22830      */
22831     /**
22832      * @event specialkey
22833      * @hide
22834      */
22835     /**
22836      * @cfg {String} fieldClass @hide
22837      */
22838     /**
22839      * @cfg {String} focusClass @hide
22840      */
22841     /**
22842      * @cfg {String} autoCreate @hide
22843      */
22844     /**
22845      * @cfg {String} inputType @hide
22846      */
22847     /**
22848      * @cfg {String} invalidClass @hide
22849      */
22850     /**
22851      * @cfg {String} invalidText @hide
22852      */
22853     /**
22854      * @cfg {String} msgFx @hide
22855      */
22856     /**
22857      * @cfg {String} validateOnBlur @hide
22858      */
22859 });
22860  
22861     // <script type="text/javascript">
22862 /*
22863  * Based on
22864  * Ext JS Library 1.1.1
22865  * Copyright(c) 2006-2007, Ext JS, LLC.
22866  *  
22867  
22868  */
22869
22870 /**
22871  * @class Roo.form.HtmlEditorToolbar1
22872  * Basic Toolbar
22873  * 
22874  * Usage:
22875  *
22876  new Roo.form.HtmlEditor({
22877     ....
22878     toolbars : [
22879         new Roo.form.HtmlEditorToolbar1({
22880             disable : { fonts: 1 , format: 1, ..., ... , ...],
22881             btns : [ .... ]
22882         })
22883     }
22884      
22885  * 
22886  * @cfg {Object} disable List of elements to disable..
22887  * @cfg {Array} btns List of additional buttons.
22888  * 
22889  * 
22890  * NEEDS Extra CSS? 
22891  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22892  */
22893  
22894 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22895 {
22896     
22897     Roo.apply(this, config);
22898     
22899     // default disabled, based on 'good practice'..
22900     this.disable = this.disable || {};
22901     Roo.applyIf(this.disable, {
22902         fontSize : true,
22903         colors : true,
22904         specialElements : true
22905     });
22906     
22907     
22908     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22909     // dont call parent... till later.
22910 }
22911
22912 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22913     
22914     tb: false,
22915     
22916     rendered: false,
22917     
22918     editor : false,
22919     editorcore : false,
22920     /**
22921      * @cfg {Object} disable  List of toolbar elements to disable
22922          
22923      */
22924     disable : false,
22925     
22926     
22927      /**
22928      * @cfg {String} createLinkText The default text for the create link prompt
22929      */
22930     createLinkText : 'Please enter the URL for the link:',
22931     /**
22932      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22933      */
22934     defaultLinkValue : 'http:/'+'/',
22935    
22936     
22937       /**
22938      * @cfg {Array} fontFamilies An array of available font families
22939      */
22940     fontFamilies : [
22941         'Arial',
22942         'Courier New',
22943         'Tahoma',
22944         'Times New Roman',
22945         'Verdana'
22946     ],
22947     
22948     specialChars : [
22949            "&#169;",
22950           "&#174;",     
22951           "&#8482;",    
22952           "&#163;" ,    
22953          // "&#8212;",    
22954           "&#8230;",    
22955           "&#247;" ,    
22956         //  "&#225;" ,     ?? a acute?
22957            "&#8364;"    , //Euro
22958        //   "&#8220;"    ,
22959         //  "&#8221;"    ,
22960         //  "&#8226;"    ,
22961           "&#176;"  //   , // degrees
22962
22963          // "&#233;"     , // e ecute
22964          // "&#250;"     , // u ecute?
22965     ],
22966     
22967     specialElements : [
22968         {
22969             text: "Insert Table",
22970             xtype: 'MenuItem',
22971             xns : Roo.Menu,
22972             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22973                 
22974         },
22975         {    
22976             text: "Insert Image",
22977             xtype: 'MenuItem',
22978             xns : Roo.Menu,
22979             ihtml : '<img src="about:blank"/>'
22980             
22981         }
22982         
22983          
22984     ],
22985     
22986     
22987     inputElements : [ 
22988             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22989             "input:submit", "input:button", "select", "textarea", "label" ],
22990     formats : [
22991         ["p"] ,  
22992         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22993         ["pre"],[ "code"], 
22994         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22995         ['div'],['span'],
22996         ['sup'],['sub']
22997     ],
22998     
22999     cleanStyles : [
23000         "font-size"
23001     ],
23002      /**
23003      * @cfg {String} defaultFont default font to use.
23004      */
23005     defaultFont: 'tahoma',
23006    
23007     fontSelect : false,
23008     
23009     
23010     formatCombo : false,
23011     
23012     init : function(editor)
23013     {
23014         this.editor = editor;
23015         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23016         var editorcore = this.editorcore;
23017         
23018         var _t = this;
23019         
23020         var fid = editorcore.frameId;
23021         var etb = this;
23022         function btn(id, toggle, handler){
23023             var xid = fid + '-'+ id ;
23024             return {
23025                 id : xid,
23026                 cmd : id,
23027                 cls : 'x-btn-icon x-edit-'+id,
23028                 enableToggle:toggle !== false,
23029                 scope: _t, // was editor...
23030                 handler:handler||_t.relayBtnCmd,
23031                 clickEvent:'mousedown',
23032                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23033                 tabIndex:-1
23034             };
23035         }
23036         
23037         
23038         
23039         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23040         this.tb = tb;
23041          // stop form submits
23042         tb.el.on('click', function(e){
23043             e.preventDefault(); // what does this do?
23044         });
23045
23046         if(!this.disable.font) { // && !Roo.isSafari){
23047             /* why no safari for fonts 
23048             editor.fontSelect = tb.el.createChild({
23049                 tag:'select',
23050                 tabIndex: -1,
23051                 cls:'x-font-select',
23052                 html: this.createFontOptions()
23053             });
23054             
23055             editor.fontSelect.on('change', function(){
23056                 var font = editor.fontSelect.dom.value;
23057                 editor.relayCmd('fontname', font);
23058                 editor.deferFocus();
23059             }, editor);
23060             
23061             tb.add(
23062                 editor.fontSelect.dom,
23063                 '-'
23064             );
23065             */
23066             
23067         };
23068         if(!this.disable.formats){
23069             this.formatCombo = new Roo.form.ComboBox({
23070                 store: new Roo.data.SimpleStore({
23071                     id : 'tag',
23072                     fields: ['tag'],
23073                     data : this.formats // from states.js
23074                 }),
23075                 blockFocus : true,
23076                 name : '',
23077                 //autoCreate : {tag: "div",  size: "20"},
23078                 displayField:'tag',
23079                 typeAhead: false,
23080                 mode: 'local',
23081                 editable : false,
23082                 triggerAction: 'all',
23083                 emptyText:'Add tag',
23084                 selectOnFocus:true,
23085                 width:135,
23086                 listeners : {
23087                     'select': function(c, r, i) {
23088                         editorcore.insertTag(r.get('tag'));
23089                         editor.focus();
23090                     }
23091                 }
23092
23093             });
23094             tb.addField(this.formatCombo);
23095             
23096         }
23097         
23098         if(!this.disable.format){
23099             tb.add(
23100                 btn('bold'),
23101                 btn('italic'),
23102                 btn('underline'),
23103                 btn('strikethrough')
23104             );
23105         };
23106         if(!this.disable.fontSize){
23107             tb.add(
23108                 '-',
23109                 
23110                 
23111                 btn('increasefontsize', false, editorcore.adjustFont),
23112                 btn('decreasefontsize', false, editorcore.adjustFont)
23113             );
23114         };
23115         
23116         
23117         if(!this.disable.colors){
23118             tb.add(
23119                 '-', {
23120                     id:editorcore.frameId +'-forecolor',
23121                     cls:'x-btn-icon x-edit-forecolor',
23122                     clickEvent:'mousedown',
23123                     tooltip: this.buttonTips['forecolor'] || undefined,
23124                     tabIndex:-1,
23125                     menu : new Roo.menu.ColorMenu({
23126                         allowReselect: true,
23127                         focus: Roo.emptyFn,
23128                         value:'000000',
23129                         plain:true,
23130                         selectHandler: function(cp, color){
23131                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23132                             editor.deferFocus();
23133                         },
23134                         scope: editorcore,
23135                         clickEvent:'mousedown'
23136                     })
23137                 }, {
23138                     id:editorcore.frameId +'backcolor',
23139                     cls:'x-btn-icon x-edit-backcolor',
23140                     clickEvent:'mousedown',
23141                     tooltip: this.buttonTips['backcolor'] || undefined,
23142                     tabIndex:-1,
23143                     menu : new Roo.menu.ColorMenu({
23144                         focus: Roo.emptyFn,
23145                         value:'FFFFFF',
23146                         plain:true,
23147                         allowReselect: true,
23148                         selectHandler: function(cp, color){
23149                             if(Roo.isGecko){
23150                                 editorcore.execCmd('useCSS', false);
23151                                 editorcore.execCmd('hilitecolor', color);
23152                                 editorcore.execCmd('useCSS', true);
23153                                 editor.deferFocus();
23154                             }else{
23155                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23156                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23157                                 editor.deferFocus();
23158                             }
23159                         },
23160                         scope:editorcore,
23161                         clickEvent:'mousedown'
23162                     })
23163                 }
23164             );
23165         };
23166         // now add all the items...
23167         
23168
23169         if(!this.disable.alignments){
23170             tb.add(
23171                 '-',
23172                 btn('justifyleft'),
23173                 btn('justifycenter'),
23174                 btn('justifyright')
23175             );
23176         };
23177
23178         //if(!Roo.isSafari){
23179             if(!this.disable.links){
23180                 tb.add(
23181                     '-',
23182                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23183                 );
23184             };
23185
23186             if(!this.disable.lists){
23187                 tb.add(
23188                     '-',
23189                     btn('insertorderedlist'),
23190                     btn('insertunorderedlist')
23191                 );
23192             }
23193             if(!this.disable.sourceEdit){
23194                 tb.add(
23195                     '-',
23196                     btn('sourceedit', true, function(btn){
23197                         this.toggleSourceEdit(btn.pressed);
23198                     })
23199                 );
23200             }
23201         //}
23202         
23203         var smenu = { };
23204         // special menu.. - needs to be tidied up..
23205         if (!this.disable.special) {
23206             smenu = {
23207                 text: "&#169;",
23208                 cls: 'x-edit-none',
23209                 
23210                 menu : {
23211                     items : []
23212                 }
23213             };
23214             for (var i =0; i < this.specialChars.length; i++) {
23215                 smenu.menu.items.push({
23216                     
23217                     html: this.specialChars[i],
23218                     handler: function(a,b) {
23219                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23220                         //editor.insertAtCursor(a.html);
23221                         
23222                     },
23223                     tabIndex:-1
23224                 });
23225             }
23226             
23227             
23228             tb.add(smenu);
23229             
23230             
23231         }
23232         
23233         var cmenu = { };
23234         if (!this.disable.cleanStyles) {
23235             cmenu = {
23236                 cls: 'x-btn-icon x-btn-clear',
23237                 
23238                 menu : {
23239                     items : []
23240                 }
23241             };
23242             for (var i =0; i < this.cleanStyles.length; i++) {
23243                 cmenu.menu.items.push({
23244                     actiontype : this.cleanStyles[i],
23245                     html: 'Remove ' + this.cleanStyles[i],
23246                     handler: function(a,b) {
23247 //                        Roo.log(a);
23248 //                        Roo.log(b);
23249                         var c = Roo.get(editorcore.doc.body);
23250                         c.select('[style]').each(function(s) {
23251                             s.dom.style.removeProperty(a.actiontype);
23252                         });
23253                         editorcore.syncValue();
23254                     },
23255                     tabIndex:-1
23256                 });
23257             }
23258              cmenu.menu.items.push({
23259                 actiontype : 'tablewidths',
23260                 html: 'Remove Table Widths',
23261                 handler: function(a,b) {
23262                     editorcore.cleanTableWidths();
23263                     editorcore.syncValue();
23264                 },
23265                 tabIndex:-1
23266             });
23267             cmenu.menu.items.push({
23268                 actiontype : 'word',
23269                 html: 'Remove MS Word Formating',
23270                 handler: function(a,b) {
23271                     editorcore.cleanWord();
23272                     editorcore.syncValue();
23273                 },
23274                 tabIndex:-1
23275             });
23276             
23277             cmenu.menu.items.push({
23278                 actiontype : 'all',
23279                 html: 'Remove All Styles',
23280                 handler: function(a,b) {
23281                     
23282                     var c = Roo.get(editorcore.doc.body);
23283                     c.select('[style]').each(function(s) {
23284                         s.dom.removeAttribute('style');
23285                     });
23286                     editorcore.syncValue();
23287                 },
23288                 tabIndex:-1
23289             });
23290             
23291             cmenu.menu.items.push({
23292                 actiontype : 'all',
23293                 html: 'Remove All CSS Classes',
23294                 handler: function(a,b) {
23295                     
23296                     var c = Roo.get(editorcore.doc.body);
23297                     c.select('[class]').each(function(s) {
23298                         s.dom.removeAttribute('class');
23299                     });
23300                     editorcore.cleanWord();
23301                     editorcore.syncValue();
23302                 },
23303                 tabIndex:-1
23304             });
23305             
23306              cmenu.menu.items.push({
23307                 actiontype : 'tidy',
23308                 html: 'Tidy HTML Source',
23309                 handler: function(a,b) {
23310                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23311                     editorcore.syncValue();
23312                 },
23313                 tabIndex:-1
23314             });
23315             
23316             
23317             tb.add(cmenu);
23318         }
23319          
23320         if (!this.disable.specialElements) {
23321             var semenu = {
23322                 text: "Other;",
23323                 cls: 'x-edit-none',
23324                 menu : {
23325                     items : []
23326                 }
23327             };
23328             for (var i =0; i < this.specialElements.length; i++) {
23329                 semenu.menu.items.push(
23330                     Roo.apply({ 
23331                         handler: function(a,b) {
23332                             editor.insertAtCursor(this.ihtml);
23333                         }
23334                     }, this.specialElements[i])
23335                 );
23336                     
23337             }
23338             
23339             tb.add(semenu);
23340             
23341             
23342         }
23343          
23344         
23345         if (this.btns) {
23346             for(var i =0; i< this.btns.length;i++) {
23347                 var b = Roo.factory(this.btns[i],Roo.form);
23348                 b.cls =  'x-edit-none';
23349                 
23350                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23351                     b.cls += ' x-init-enable';
23352                 }
23353                 
23354                 b.scope = editorcore;
23355                 tb.add(b);
23356             }
23357         
23358         }
23359         
23360         
23361         
23362         // disable everything...
23363         
23364         this.tb.items.each(function(item){
23365             
23366            if(
23367                 item.id != editorcore.frameId+ '-sourceedit' && 
23368                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23369             ){
23370                 
23371                 item.disable();
23372             }
23373         });
23374         this.rendered = true;
23375         
23376         // the all the btns;
23377         editor.on('editorevent', this.updateToolbar, this);
23378         // other toolbars need to implement this..
23379         //editor.on('editmodechange', this.updateToolbar, this);
23380     },
23381     
23382     
23383     relayBtnCmd : function(btn) {
23384         this.editorcore.relayCmd(btn.cmd);
23385     },
23386     // private used internally
23387     createLink : function(){
23388         Roo.log("create link?");
23389         var url = prompt(this.createLinkText, this.defaultLinkValue);
23390         if(url && url != 'http:/'+'/'){
23391             this.editorcore.relayCmd('createlink', url);
23392         }
23393     },
23394
23395     
23396     /**
23397      * Protected method that will not generally be called directly. It triggers
23398      * a toolbar update by reading the markup state of the current selection in the editor.
23399      */
23400     updateToolbar: function(){
23401
23402         if(!this.editorcore.activated){
23403             this.editor.onFirstFocus();
23404             return;
23405         }
23406
23407         var btns = this.tb.items.map, 
23408             doc = this.editorcore.doc,
23409             frameId = this.editorcore.frameId;
23410
23411         if(!this.disable.font && !Roo.isSafari){
23412             /*
23413             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23414             if(name != this.fontSelect.dom.value){
23415                 this.fontSelect.dom.value = name;
23416             }
23417             */
23418         }
23419         if(!this.disable.format){
23420             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23421             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23422             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23423             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23424         }
23425         if(!this.disable.alignments){
23426             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23427             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23428             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23429         }
23430         if(!Roo.isSafari && !this.disable.lists){
23431             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23432             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23433         }
23434         
23435         var ans = this.editorcore.getAllAncestors();
23436         if (this.formatCombo) {
23437             
23438             
23439             var store = this.formatCombo.store;
23440             this.formatCombo.setValue("");
23441             for (var i =0; i < ans.length;i++) {
23442                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23443                     // select it..
23444                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23445                     break;
23446                 }
23447             }
23448         }
23449         
23450         
23451         
23452         // hides menus... - so this cant be on a menu...
23453         Roo.menu.MenuMgr.hideAll();
23454
23455         //this.editorsyncValue();
23456     },
23457    
23458     
23459     createFontOptions : function(){
23460         var buf = [], fs = this.fontFamilies, ff, lc;
23461         
23462         
23463         
23464         for(var i = 0, len = fs.length; i< len; i++){
23465             ff = fs[i];
23466             lc = ff.toLowerCase();
23467             buf.push(
23468                 '<option value="',lc,'" style="font-family:',ff,';"',
23469                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23470                     ff,
23471                 '</option>'
23472             );
23473         }
23474         return buf.join('');
23475     },
23476     
23477     toggleSourceEdit : function(sourceEditMode){
23478         
23479         Roo.log("toolbar toogle");
23480         if(sourceEditMode === undefined){
23481             sourceEditMode = !this.sourceEditMode;
23482         }
23483         this.sourceEditMode = sourceEditMode === true;
23484         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23485         // just toggle the button?
23486         if(btn.pressed !== this.sourceEditMode){
23487             btn.toggle(this.sourceEditMode);
23488             return;
23489         }
23490         
23491         if(sourceEditMode){
23492             Roo.log("disabling buttons");
23493             this.tb.items.each(function(item){
23494                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23495                     item.disable();
23496                 }
23497             });
23498           
23499         }else{
23500             Roo.log("enabling buttons");
23501             if(this.editorcore.initialized){
23502                 this.tb.items.each(function(item){
23503                     item.enable();
23504                 });
23505             }
23506             
23507         }
23508         Roo.log("calling toggole on editor");
23509         // tell the editor that it's been pressed..
23510         this.editor.toggleSourceEdit(sourceEditMode);
23511        
23512     },
23513      /**
23514      * Object collection of toolbar tooltips for the buttons in the editor. The key
23515      * is the command id associated with that button and the value is a valid QuickTips object.
23516      * For example:
23517 <pre><code>
23518 {
23519     bold : {
23520         title: 'Bold (Ctrl+B)',
23521         text: 'Make the selected text bold.',
23522         cls: 'x-html-editor-tip'
23523     },
23524     italic : {
23525         title: 'Italic (Ctrl+I)',
23526         text: 'Make the selected text italic.',
23527         cls: 'x-html-editor-tip'
23528     },
23529     ...
23530 </code></pre>
23531     * @type Object
23532      */
23533     buttonTips : {
23534         bold : {
23535             title: 'Bold (Ctrl+B)',
23536             text: 'Make the selected text bold.',
23537             cls: 'x-html-editor-tip'
23538         },
23539         italic : {
23540             title: 'Italic (Ctrl+I)',
23541             text: 'Make the selected text italic.',
23542             cls: 'x-html-editor-tip'
23543         },
23544         underline : {
23545             title: 'Underline (Ctrl+U)',
23546             text: 'Underline the selected text.',
23547             cls: 'x-html-editor-tip'
23548         },
23549         strikethrough : {
23550             title: 'Strikethrough',
23551             text: 'Strikethrough the selected text.',
23552             cls: 'x-html-editor-tip'
23553         },
23554         increasefontsize : {
23555             title: 'Grow Text',
23556             text: 'Increase the font size.',
23557             cls: 'x-html-editor-tip'
23558         },
23559         decreasefontsize : {
23560             title: 'Shrink Text',
23561             text: 'Decrease the font size.',
23562             cls: 'x-html-editor-tip'
23563         },
23564         backcolor : {
23565             title: 'Text Highlight Color',
23566             text: 'Change the background color of the selected text.',
23567             cls: 'x-html-editor-tip'
23568         },
23569         forecolor : {
23570             title: 'Font Color',
23571             text: 'Change the color of the selected text.',
23572             cls: 'x-html-editor-tip'
23573         },
23574         justifyleft : {
23575             title: 'Align Text Left',
23576             text: 'Align text to the left.',
23577             cls: 'x-html-editor-tip'
23578         },
23579         justifycenter : {
23580             title: 'Center Text',
23581             text: 'Center text in the editor.',
23582             cls: 'x-html-editor-tip'
23583         },
23584         justifyright : {
23585             title: 'Align Text Right',
23586             text: 'Align text to the right.',
23587             cls: 'x-html-editor-tip'
23588         },
23589         insertunorderedlist : {
23590             title: 'Bullet List',
23591             text: 'Start a bulleted list.',
23592             cls: 'x-html-editor-tip'
23593         },
23594         insertorderedlist : {
23595             title: 'Numbered List',
23596             text: 'Start a numbered list.',
23597             cls: 'x-html-editor-tip'
23598         },
23599         createlink : {
23600             title: 'Hyperlink',
23601             text: 'Make the selected text a hyperlink.',
23602             cls: 'x-html-editor-tip'
23603         },
23604         sourceedit : {
23605             title: 'Source Edit',
23606             text: 'Switch to source editing mode.',
23607             cls: 'x-html-editor-tip'
23608         }
23609     },
23610     // private
23611     onDestroy : function(){
23612         if(this.rendered){
23613             
23614             this.tb.items.each(function(item){
23615                 if(item.menu){
23616                     item.menu.removeAll();
23617                     if(item.menu.el){
23618                         item.menu.el.destroy();
23619                     }
23620                 }
23621                 item.destroy();
23622             });
23623              
23624         }
23625     },
23626     onFirstFocus: function() {
23627         this.tb.items.each(function(item){
23628            item.enable();
23629         });
23630     }
23631 });
23632
23633
23634
23635
23636 // <script type="text/javascript">
23637 /*
23638  * Based on
23639  * Ext JS Library 1.1.1
23640  * Copyright(c) 2006-2007, Ext JS, LLC.
23641  *  
23642  
23643  */
23644
23645  
23646 /**
23647  * @class Roo.form.HtmlEditor.ToolbarContext
23648  * Context Toolbar
23649  * 
23650  * Usage:
23651  *
23652  new Roo.form.HtmlEditor({
23653     ....
23654     toolbars : [
23655         { xtype: 'ToolbarStandard', styles : {} }
23656         { xtype: 'ToolbarContext', disable : {} }
23657     ]
23658 })
23659
23660      
23661  * 
23662  * @config : {Object} disable List of elements to disable.. (not done yet.)
23663  * @config : {Object} styles  Map of styles available.
23664  * 
23665  */
23666
23667 Roo.form.HtmlEditor.ToolbarContext = function(config)
23668 {
23669     
23670     Roo.apply(this, config);
23671     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23672     // dont call parent... till later.
23673     this.styles = this.styles || {};
23674 }
23675
23676  
23677
23678 Roo.form.HtmlEditor.ToolbarContext.types = {
23679     'IMG' : {
23680         width : {
23681             title: "Width",
23682             width: 40
23683         },
23684         height:  {
23685             title: "Height",
23686             width: 40
23687         },
23688         align: {
23689             title: "Align",
23690             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23691             width : 80
23692             
23693         },
23694         border: {
23695             title: "Border",
23696             width: 40
23697         },
23698         alt: {
23699             title: "Alt",
23700             width: 120
23701         },
23702         src : {
23703             title: "Src",
23704             width: 220
23705         }
23706         
23707     },
23708     'A' : {
23709         name : {
23710             title: "Name",
23711             width: 50
23712         },
23713         target:  {
23714             title: "Target",
23715             width: 120
23716         },
23717         href:  {
23718             title: "Href",
23719             width: 220
23720         } // border?
23721         
23722     },
23723     'TABLE' : {
23724         rows : {
23725             title: "Rows",
23726             width: 20
23727         },
23728         cols : {
23729             title: "Cols",
23730             width: 20
23731         },
23732         width : {
23733             title: "Width",
23734             width: 40
23735         },
23736         height : {
23737             title: "Height",
23738             width: 40
23739         },
23740         border : {
23741             title: "Border",
23742             width: 20
23743         }
23744     },
23745     'TD' : {
23746         width : {
23747             title: "Width",
23748             width: 40
23749         },
23750         height : {
23751             title: "Height",
23752             width: 40
23753         },   
23754         align: {
23755             title: "Align",
23756             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23757             width: 80
23758         },
23759         valign: {
23760             title: "Valign",
23761             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23762             width: 80
23763         },
23764         colspan: {
23765             title: "Colspan",
23766             width: 20
23767             
23768         },
23769          'font-family'  : {
23770             title : "Font",
23771             style : 'fontFamily',
23772             displayField: 'display',
23773             optname : 'font-family',
23774             width: 140
23775         }
23776     },
23777     'INPUT' : {
23778         name : {
23779             title: "name",
23780             width: 120
23781         },
23782         value : {
23783             title: "Value",
23784             width: 120
23785         },
23786         width : {
23787             title: "Width",
23788             width: 40
23789         }
23790     },
23791     'LABEL' : {
23792         'for' : {
23793             title: "For",
23794             width: 120
23795         }
23796     },
23797     'TEXTAREA' : {
23798           name : {
23799             title: "name",
23800             width: 120
23801         },
23802         rows : {
23803             title: "Rows",
23804             width: 20
23805         },
23806         cols : {
23807             title: "Cols",
23808             width: 20
23809         }
23810     },
23811     'SELECT' : {
23812         name : {
23813             title: "name",
23814             width: 120
23815         },
23816         selectoptions : {
23817             title: "Options",
23818             width: 200
23819         }
23820     },
23821     
23822     // should we really allow this??
23823     // should this just be 
23824     'BODY' : {
23825         title : {
23826             title: "Title",
23827             width: 200,
23828             disabled : true
23829         }
23830     },
23831     'SPAN' : {
23832         'font-family'  : {
23833             title : "Font",
23834             style : 'fontFamily',
23835             displayField: 'display',
23836             optname : 'font-family',
23837             width: 140
23838         }
23839     },
23840     'DIV' : {
23841         'font-family'  : {
23842             title : "Font",
23843             style : 'fontFamily',
23844             displayField: 'display',
23845             optname : 'font-family',
23846             width: 140
23847         }
23848     },
23849      'P' : {
23850         'font-family'  : {
23851             title : "Font",
23852             style : 'fontFamily',
23853             displayField: 'display',
23854             optname : 'font-family',
23855             width: 140
23856         }
23857     },
23858     
23859     '*' : {
23860         // empty..
23861     }
23862
23863 };
23864
23865 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23866 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23867
23868 Roo.form.HtmlEditor.ToolbarContext.options = {
23869         'font-family'  : [ 
23870                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23871                 [ 'Courier New', 'Courier New'],
23872                 [ 'Tahoma', 'Tahoma'],
23873                 [ 'Times New Roman,serif', 'Times'],
23874                 [ 'Verdana','Verdana' ]
23875         ]
23876 };
23877
23878 // fixme - these need to be configurable..
23879  
23880
23881 //Roo.form.HtmlEditor.ToolbarContext.types
23882
23883
23884 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23885     
23886     tb: false,
23887     
23888     rendered: false,
23889     
23890     editor : false,
23891     editorcore : false,
23892     /**
23893      * @cfg {Object} disable  List of toolbar elements to disable
23894          
23895      */
23896     disable : false,
23897     /**
23898      * @cfg {Object} styles List of styles 
23899      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23900      *
23901      * These must be defined in the page, so they get rendered correctly..
23902      * .headline { }
23903      * TD.underline { }
23904      * 
23905      */
23906     styles : false,
23907     
23908     options: false,
23909     
23910     toolbars : false,
23911     
23912     init : function(editor)
23913     {
23914         this.editor = editor;
23915         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23916         var editorcore = this.editorcore;
23917         
23918         var fid = editorcore.frameId;
23919         var etb = this;
23920         function btn(id, toggle, handler){
23921             var xid = fid + '-'+ id ;
23922             return {
23923                 id : xid,
23924                 cmd : id,
23925                 cls : 'x-btn-icon x-edit-'+id,
23926                 enableToggle:toggle !== false,
23927                 scope: editorcore, // was editor...
23928                 handler:handler||editorcore.relayBtnCmd,
23929                 clickEvent:'mousedown',
23930                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23931                 tabIndex:-1
23932             };
23933         }
23934         // create a new element.
23935         var wdiv = editor.wrap.createChild({
23936                 tag: 'div'
23937             }, editor.wrap.dom.firstChild.nextSibling, true);
23938         
23939         // can we do this more than once??
23940         
23941          // stop form submits
23942       
23943  
23944         // disable everything...
23945         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23946         this.toolbars = {};
23947            
23948         for (var i in  ty) {
23949           
23950             this.toolbars[i] = this.buildToolbar(ty[i],i);
23951         }
23952         this.tb = this.toolbars.BODY;
23953         this.tb.el.show();
23954         this.buildFooter();
23955         this.footer.show();
23956         editor.on('hide', function( ) { this.footer.hide() }, this);
23957         editor.on('show', function( ) { this.footer.show() }, this);
23958         
23959          
23960         this.rendered = true;
23961         
23962         // the all the btns;
23963         editor.on('editorevent', this.updateToolbar, this);
23964         // other toolbars need to implement this..
23965         //editor.on('editmodechange', this.updateToolbar, this);
23966     },
23967     
23968     
23969     
23970     /**
23971      * Protected method that will not generally be called directly. It triggers
23972      * a toolbar update by reading the markup state of the current selection in the editor.
23973      *
23974      * Note you can force an update by calling on('editorevent', scope, false)
23975      */
23976     updateToolbar: function(editor,ev,sel){
23977
23978         //Roo.log(ev);
23979         // capture mouse up - this is handy for selecting images..
23980         // perhaps should go somewhere else...
23981         if(!this.editorcore.activated){
23982              this.editor.onFirstFocus();
23983             return;
23984         }
23985         
23986         
23987         
23988         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23989         // selectNode - might want to handle IE?
23990         if (ev &&
23991             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23992             ev.target && ev.target.tagName == 'IMG') {
23993             // they have click on an image...
23994             // let's see if we can change the selection...
23995             sel = ev.target;
23996          
23997               var nodeRange = sel.ownerDocument.createRange();
23998             try {
23999                 nodeRange.selectNode(sel);
24000             } catch (e) {
24001                 nodeRange.selectNodeContents(sel);
24002             }
24003             //nodeRange.collapse(true);
24004             var s = this.editorcore.win.getSelection();
24005             s.removeAllRanges();
24006             s.addRange(nodeRange);
24007         }  
24008         
24009       
24010         var updateFooter = sel ? false : true;
24011         
24012         
24013         var ans = this.editorcore.getAllAncestors();
24014         
24015         // pick
24016         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24017         
24018         if (!sel) { 
24019             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24020             sel = sel ? sel : this.editorcore.doc.body;
24021             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24022             
24023         }
24024         // pick a menu that exists..
24025         var tn = sel.tagName.toUpperCase();
24026         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24027         
24028         tn = sel.tagName.toUpperCase();
24029         
24030         var lastSel = this.tb.selectedNode;
24031         
24032         this.tb.selectedNode = sel;
24033         
24034         // if current menu does not match..
24035         
24036         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24037                 
24038             this.tb.el.hide();
24039             ///console.log("show: " + tn);
24040             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24041             this.tb.el.show();
24042             // update name
24043             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24044             
24045             
24046             // update attributes
24047             if (this.tb.fields) {
24048                 this.tb.fields.each(function(e) {
24049                     if (e.stylename) {
24050                         e.setValue(sel.style[e.stylename]);
24051                         return;
24052                     } 
24053                    e.setValue(sel.getAttribute(e.attrname));
24054                 });
24055             }
24056             
24057             var hasStyles = false;
24058             for(var i in this.styles) {
24059                 hasStyles = true;
24060                 break;
24061             }
24062             
24063             // update styles
24064             if (hasStyles) { 
24065                 var st = this.tb.fields.item(0);
24066                 
24067                 st.store.removeAll();
24068                
24069                 
24070                 var cn = sel.className.split(/\s+/);
24071                 
24072                 var avs = [];
24073                 if (this.styles['*']) {
24074                     
24075                     Roo.each(this.styles['*'], function(v) {
24076                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24077                     });
24078                 }
24079                 if (this.styles[tn]) { 
24080                     Roo.each(this.styles[tn], function(v) {
24081                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24082                     });
24083                 }
24084                 
24085                 st.store.loadData(avs);
24086                 st.collapse();
24087                 st.setValue(cn);
24088             }
24089             // flag our selected Node.
24090             this.tb.selectedNode = sel;
24091            
24092            
24093             Roo.menu.MenuMgr.hideAll();
24094
24095         }
24096         
24097         if (!updateFooter) {
24098             //this.footDisp.dom.innerHTML = ''; 
24099             return;
24100         }
24101         // update the footer
24102         //
24103         var html = '';
24104         
24105         this.footerEls = ans.reverse();
24106         Roo.each(this.footerEls, function(a,i) {
24107             if (!a) { return; }
24108             html += html.length ? ' &gt; '  :  '';
24109             
24110             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24111             
24112         });
24113        
24114         // 
24115         var sz = this.footDisp.up('td').getSize();
24116         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24117         this.footDisp.dom.style.marginLeft = '5px';
24118         
24119         this.footDisp.dom.style.overflow = 'hidden';
24120         
24121         this.footDisp.dom.innerHTML = html;
24122             
24123         //this.editorsyncValue();
24124     },
24125      
24126     
24127    
24128        
24129     // private
24130     onDestroy : function(){
24131         if(this.rendered){
24132             
24133             this.tb.items.each(function(item){
24134                 if(item.menu){
24135                     item.menu.removeAll();
24136                     if(item.menu.el){
24137                         item.menu.el.destroy();
24138                     }
24139                 }
24140                 item.destroy();
24141             });
24142              
24143         }
24144     },
24145     onFirstFocus: function() {
24146         // need to do this for all the toolbars..
24147         this.tb.items.each(function(item){
24148            item.enable();
24149         });
24150     },
24151     buildToolbar: function(tlist, nm)
24152     {
24153         var editor = this.editor;
24154         var editorcore = this.editorcore;
24155          // create a new element.
24156         var wdiv = editor.wrap.createChild({
24157                 tag: 'div'
24158             }, editor.wrap.dom.firstChild.nextSibling, true);
24159         
24160        
24161         var tb = new Roo.Toolbar(wdiv);
24162         // add the name..
24163         
24164         tb.add(nm+ ":&nbsp;");
24165         
24166         var styles = [];
24167         for(var i in this.styles) {
24168             styles.push(i);
24169         }
24170         
24171         // styles...
24172         if (styles && styles.length) {
24173             
24174             // this needs a multi-select checkbox...
24175             tb.addField( new Roo.form.ComboBox({
24176                 store: new Roo.data.SimpleStore({
24177                     id : 'val',
24178                     fields: ['val', 'selected'],
24179                     data : [] 
24180                 }),
24181                 name : '-roo-edit-className',
24182                 attrname : 'className',
24183                 displayField: 'val',
24184                 typeAhead: false,
24185                 mode: 'local',
24186                 editable : false,
24187                 triggerAction: 'all',
24188                 emptyText:'Select Style',
24189                 selectOnFocus:true,
24190                 width: 130,
24191                 listeners : {
24192                     'select': function(c, r, i) {
24193                         // initial support only for on class per el..
24194                         tb.selectedNode.className =  r ? r.get('val') : '';
24195                         editorcore.syncValue();
24196                     }
24197                 }
24198     
24199             }));
24200         }
24201         
24202         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24203         var tbops = tbc.options;
24204         
24205         for (var i in tlist) {
24206             
24207             var item = tlist[i];
24208             tb.add(item.title + ":&nbsp;");
24209             
24210             
24211             //optname == used so you can configure the options available..
24212             var opts = item.opts ? item.opts : false;
24213             if (item.optname) {
24214                 opts = tbops[item.optname];
24215            
24216             }
24217             
24218             if (opts) {
24219                 // opts == pulldown..
24220                 tb.addField( new Roo.form.ComboBox({
24221                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24222                         id : 'val',
24223                         fields: ['val', 'display'],
24224                         data : opts  
24225                     }),
24226                     name : '-roo-edit-' + i,
24227                     attrname : i,
24228                     stylename : item.style ? item.style : false,
24229                     displayField: item.displayField ? item.displayField : 'val',
24230                     valueField :  'val',
24231                     typeAhead: false,
24232                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24233                     editable : false,
24234                     triggerAction: 'all',
24235                     emptyText:'Select',
24236                     selectOnFocus:true,
24237                     width: item.width ? item.width  : 130,
24238                     listeners : {
24239                         'select': function(c, r, i) {
24240                             if (c.stylename) {
24241                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24242                                 return;
24243                             }
24244                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24245                         }
24246                     }
24247
24248                 }));
24249                 continue;
24250                     
24251                  
24252                 
24253                 tb.addField( new Roo.form.TextField({
24254                     name: i,
24255                     width: 100,
24256                     //allowBlank:false,
24257                     value: ''
24258                 }));
24259                 continue;
24260             }
24261             tb.addField( new Roo.form.TextField({
24262                 name: '-roo-edit-' + i,
24263                 attrname : i,
24264                 
24265                 width: item.width,
24266                 //allowBlank:true,
24267                 value: '',
24268                 listeners: {
24269                     'change' : function(f, nv, ov) {
24270                         tb.selectedNode.setAttribute(f.attrname, nv);
24271                         editorcore.syncValue();
24272                     }
24273                 }
24274             }));
24275              
24276         }
24277         
24278         var _this = this;
24279         
24280         if(nm == 'BODY'){
24281             tb.addSeparator();
24282         
24283             tb.addButton( {
24284                 text: 'Stylesheets',
24285
24286                 listeners : {
24287                     click : function ()
24288                     {
24289                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24290                     }
24291                 }
24292             });
24293         }
24294         
24295         tb.addFill();
24296         tb.addButton( {
24297             text: 'Remove Tag',
24298     
24299             listeners : {
24300                 click : function ()
24301                 {
24302                     // remove
24303                     // undo does not work.
24304                      
24305                     var sn = tb.selectedNode;
24306                     
24307                     var pn = sn.parentNode;
24308                     
24309                     var stn =  sn.childNodes[0];
24310                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24311                     while (sn.childNodes.length) {
24312                         var node = sn.childNodes[0];
24313                         sn.removeChild(node);
24314                         //Roo.log(node);
24315                         pn.insertBefore(node, sn);
24316                         
24317                     }
24318                     pn.removeChild(sn);
24319                     var range = editorcore.createRange();
24320         
24321                     range.setStart(stn,0);
24322                     range.setEnd(en,0); //????
24323                     //range.selectNode(sel);
24324                     
24325                     
24326                     var selection = editorcore.getSelection();
24327                     selection.removeAllRanges();
24328                     selection.addRange(range);
24329                     
24330                     
24331                     
24332                     //_this.updateToolbar(null, null, pn);
24333                     _this.updateToolbar(null, null, null);
24334                     _this.footDisp.dom.innerHTML = ''; 
24335                 }
24336             }
24337             
24338                     
24339                 
24340             
24341         });
24342         
24343         
24344         tb.el.on('click', function(e){
24345             e.preventDefault(); // what does this do?
24346         });
24347         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24348         tb.el.hide();
24349         tb.name = nm;
24350         // dont need to disable them... as they will get hidden
24351         return tb;
24352          
24353         
24354     },
24355     buildFooter : function()
24356     {
24357         
24358         var fel = this.editor.wrap.createChild();
24359         this.footer = new Roo.Toolbar(fel);
24360         // toolbar has scrolly on left / right?
24361         var footDisp= new Roo.Toolbar.Fill();
24362         var _t = this;
24363         this.footer.add(
24364             {
24365                 text : '&lt;',
24366                 xtype: 'Button',
24367                 handler : function() {
24368                     _t.footDisp.scrollTo('left',0,true)
24369                 }
24370             }
24371         );
24372         this.footer.add( footDisp );
24373         this.footer.add( 
24374             {
24375                 text : '&gt;',
24376                 xtype: 'Button',
24377                 handler : function() {
24378                     // no animation..
24379                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24380                 }
24381             }
24382         );
24383         var fel = Roo.get(footDisp.el);
24384         fel.addClass('x-editor-context');
24385         this.footDispWrap = fel; 
24386         this.footDispWrap.overflow  = 'hidden';
24387         
24388         this.footDisp = fel.createChild();
24389         this.footDispWrap.on('click', this.onContextClick, this)
24390         
24391         
24392     },
24393     onContextClick : function (ev,dom)
24394     {
24395         ev.preventDefault();
24396         var  cn = dom.className;
24397         //Roo.log(cn);
24398         if (!cn.match(/x-ed-loc-/)) {
24399             return;
24400         }
24401         var n = cn.split('-').pop();
24402         var ans = this.footerEls;
24403         var sel = ans[n];
24404         
24405          // pick
24406         var range = this.editorcore.createRange();
24407         
24408         range.selectNodeContents(sel);
24409         //range.selectNode(sel);
24410         
24411         
24412         var selection = this.editorcore.getSelection();
24413         selection.removeAllRanges();
24414         selection.addRange(range);
24415         
24416         
24417         
24418         this.updateToolbar(null, null, sel);
24419         
24420         
24421     }
24422     
24423     
24424     
24425     
24426     
24427 });
24428
24429
24430
24431
24432
24433 /*
24434  * Based on:
24435  * Ext JS Library 1.1.1
24436  * Copyright(c) 2006-2007, Ext JS, LLC.
24437  *
24438  * Originally Released Under LGPL - original licence link has changed is not relivant.
24439  *
24440  * Fork - LGPL
24441  * <script type="text/javascript">
24442  */
24443  
24444 /**
24445  * @class Roo.form.BasicForm
24446  * @extends Roo.util.Observable
24447  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24448  * @constructor
24449  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24450  * @param {Object} config Configuration options
24451  */
24452 Roo.form.BasicForm = function(el, config){
24453     this.allItems = [];
24454     this.childForms = [];
24455     Roo.apply(this, config);
24456     /*
24457      * The Roo.form.Field items in this form.
24458      * @type MixedCollection
24459      */
24460      
24461      
24462     this.items = new Roo.util.MixedCollection(false, function(o){
24463         return o.id || (o.id = Roo.id());
24464     });
24465     this.addEvents({
24466         /**
24467          * @event beforeaction
24468          * Fires before any action is performed. Return false to cancel the action.
24469          * @param {Form} this
24470          * @param {Action} action The action to be performed
24471          */
24472         beforeaction: true,
24473         /**
24474          * @event actionfailed
24475          * Fires when an action fails.
24476          * @param {Form} this
24477          * @param {Action} action The action that failed
24478          */
24479         actionfailed : true,
24480         /**
24481          * @event actioncomplete
24482          * Fires when an action is completed.
24483          * @param {Form} this
24484          * @param {Action} action The action that completed
24485          */
24486         actioncomplete : true
24487     });
24488     if(el){
24489         this.initEl(el);
24490     }
24491     Roo.form.BasicForm.superclass.constructor.call(this);
24492     
24493     Roo.form.BasicForm.popover.apply();
24494 };
24495
24496 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24497     /**
24498      * @cfg {String} method
24499      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24500      */
24501     /**
24502      * @cfg {DataReader} reader
24503      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24504      * This is optional as there is built-in support for processing JSON.
24505      */
24506     /**
24507      * @cfg {DataReader} errorReader
24508      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24509      * This is completely optional as there is built-in support for processing JSON.
24510      */
24511     /**
24512      * @cfg {String} url
24513      * The URL to use for form actions if one isn't supplied in the action options.
24514      */
24515     /**
24516      * @cfg {Boolean} fileUpload
24517      * Set to true if this form is a file upload.
24518      */
24519      
24520     /**
24521      * @cfg {Object} baseParams
24522      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24523      */
24524      /**
24525      
24526     /**
24527      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24528      */
24529     timeout: 30,
24530
24531     // private
24532     activeAction : null,
24533
24534     /**
24535      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24536      * or setValues() data instead of when the form was first created.
24537      */
24538     trackResetOnLoad : false,
24539     
24540     
24541     /**
24542      * childForms - used for multi-tab forms
24543      * @type {Array}
24544      */
24545     childForms : false,
24546     
24547     /**
24548      * allItems - full list of fields.
24549      * @type {Array}
24550      */
24551     allItems : false,
24552     
24553     /**
24554      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24555      * element by passing it or its id or mask the form itself by passing in true.
24556      * @type Mixed
24557      */
24558     waitMsgTarget : false,
24559     
24560     /**
24561      * @type Boolean
24562      */
24563     disableMask : false,
24564     
24565     /**
24566      * @cfg {Boolean} errorMask (true|false) default false
24567      */
24568     errorMask : false,
24569     
24570     /**
24571      * @cfg {Number} maskOffset Default 100
24572      */
24573     maskOffset : 100,
24574
24575     // private
24576     initEl : function(el){
24577         this.el = Roo.get(el);
24578         this.id = this.el.id || Roo.id();
24579         this.el.on('submit', this.onSubmit, this);
24580         this.el.addClass('x-form');
24581     },
24582
24583     // private
24584     onSubmit : function(e){
24585         e.stopEvent();
24586     },
24587
24588     /**
24589      * Returns true if client-side validation on the form is successful.
24590      * @return Boolean
24591      */
24592     isValid : function(){
24593         var valid = true;
24594         var target = false;
24595         this.items.each(function(f){
24596             if(f.validate()){
24597                 return;
24598             }
24599             
24600             valid = false;
24601                 
24602             if(!target && f.el.isVisible(true)){
24603                 target = f;
24604             }
24605         });
24606         
24607         if(this.errorMask && !valid){
24608             Roo.form.BasicForm.popover.mask(this, target);
24609         }
24610         
24611         return valid;
24612     },
24613     /**
24614      * Returns array of invalid form fields.
24615      * @return Array
24616      */
24617     
24618     invalidFields : function()
24619     {
24620         var ret = [];
24621         this.items.each(function(f){
24622             if(f.validate()){
24623                 return;
24624             }
24625             ret.push(f);
24626             
24627         });
24628         
24629         return ret;
24630     },
24631     
24632     
24633     /**
24634      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24635      * @return Boolean
24636      */
24637     isDirty : function(){
24638         var dirty = false;
24639         this.items.each(function(f){
24640            if(f.isDirty()){
24641                dirty = true;
24642                return false;
24643            }
24644         });
24645         return dirty;
24646     },
24647     
24648     /**
24649      * Returns true if any fields in this form have changed since their original load. (New version)
24650      * @return Boolean
24651      */
24652     
24653     hasChanged : function()
24654     {
24655         var dirty = false;
24656         this.items.each(function(f){
24657            if(f.hasChanged()){
24658                dirty = true;
24659                return false;
24660            }
24661         });
24662         return dirty;
24663         
24664     },
24665     /**
24666      * Resets all hasChanged to 'false' -
24667      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24668      * So hasChanged storage is only to be used for this purpose
24669      * @return Boolean
24670      */
24671     resetHasChanged : function()
24672     {
24673         this.items.each(function(f){
24674            f.resetHasChanged();
24675         });
24676         
24677     },
24678     
24679     
24680     /**
24681      * Performs a predefined action (submit or load) or custom actions you define on this form.
24682      * @param {String} actionName The name of the action type
24683      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24684      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24685      * accept other config options):
24686      * <pre>
24687 Property          Type             Description
24688 ----------------  ---------------  ----------------------------------------------------------------------------------
24689 url               String           The url for the action (defaults to the form's url)
24690 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24691 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24692 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24693                                    validate the form on the client (defaults to false)
24694      * </pre>
24695      * @return {BasicForm} this
24696      */
24697     doAction : function(action, options){
24698         if(typeof action == 'string'){
24699             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24700         }
24701         if(this.fireEvent('beforeaction', this, action) !== false){
24702             this.beforeAction(action);
24703             action.run.defer(100, action);
24704         }
24705         return this;
24706     },
24707
24708     /**
24709      * Shortcut to do a submit action.
24710      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24711      * @return {BasicForm} this
24712      */
24713     submit : function(options){
24714         this.doAction('submit', options);
24715         return this;
24716     },
24717
24718     /**
24719      * Shortcut to do a load action.
24720      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24721      * @return {BasicForm} this
24722      */
24723     load : function(options){
24724         this.doAction('load', options);
24725         return this;
24726     },
24727
24728     /**
24729      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24730      * @param {Record} record The record to edit
24731      * @return {BasicForm} this
24732      */
24733     updateRecord : function(record){
24734         record.beginEdit();
24735         var fs = record.fields;
24736         fs.each(function(f){
24737             var field = this.findField(f.name);
24738             if(field){
24739                 record.set(f.name, field.getValue());
24740             }
24741         }, this);
24742         record.endEdit();
24743         return this;
24744     },
24745
24746     /**
24747      * Loads an Roo.data.Record into this form.
24748      * @param {Record} record The record to load
24749      * @return {BasicForm} this
24750      */
24751     loadRecord : function(record){
24752         this.setValues(record.data);
24753         return this;
24754     },
24755
24756     // private
24757     beforeAction : function(action){
24758         var o = action.options;
24759         
24760         if(!this.disableMask) {
24761             if(this.waitMsgTarget === true){
24762                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24763             }else if(this.waitMsgTarget){
24764                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24765                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24766             }else {
24767                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24768             }
24769         }
24770         
24771          
24772     },
24773
24774     // private
24775     afterAction : function(action, success){
24776         this.activeAction = null;
24777         var o = action.options;
24778         
24779         if(!this.disableMask) {
24780             if(this.waitMsgTarget === true){
24781                 this.el.unmask();
24782             }else if(this.waitMsgTarget){
24783                 this.waitMsgTarget.unmask();
24784             }else{
24785                 Roo.MessageBox.updateProgress(1);
24786                 Roo.MessageBox.hide();
24787             }
24788         }
24789         
24790         if(success){
24791             if(o.reset){
24792                 this.reset();
24793             }
24794             Roo.callback(o.success, o.scope, [this, action]);
24795             this.fireEvent('actioncomplete', this, action);
24796             
24797         }else{
24798             
24799             // failure condition..
24800             // we have a scenario where updates need confirming.
24801             // eg. if a locking scenario exists..
24802             // we look for { errors : { needs_confirm : true }} in the response.
24803             if (
24804                 (typeof(action.result) != 'undefined')  &&
24805                 (typeof(action.result.errors) != 'undefined')  &&
24806                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24807            ){
24808                 var _t = this;
24809                 Roo.MessageBox.confirm(
24810                     "Change requires confirmation",
24811                     action.result.errorMsg,
24812                     function(r) {
24813                         if (r != 'yes') {
24814                             return;
24815                         }
24816                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24817                     }
24818                     
24819                 );
24820                 
24821                 
24822                 
24823                 return;
24824             }
24825             
24826             Roo.callback(o.failure, o.scope, [this, action]);
24827             // show an error message if no failed handler is set..
24828             if (!this.hasListener('actionfailed')) {
24829                 Roo.MessageBox.alert("Error",
24830                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24831                         action.result.errorMsg :
24832                         "Saving Failed, please check your entries or try again"
24833                 );
24834             }
24835             
24836             this.fireEvent('actionfailed', this, action);
24837         }
24838         
24839     },
24840
24841     /**
24842      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24843      * @param {String} id The value to search for
24844      * @return Field
24845      */
24846     findField : function(id){
24847         var field = this.items.get(id);
24848         if(!field){
24849             this.items.each(function(f){
24850                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24851                     field = f;
24852                     return false;
24853                 }
24854             });
24855         }
24856         return field || null;
24857     },
24858
24859     /**
24860      * Add a secondary form to this one, 
24861      * Used to provide tabbed forms. One form is primary, with hidden values 
24862      * which mirror the elements from the other forms.
24863      * 
24864      * @param {Roo.form.Form} form to add.
24865      * 
24866      */
24867     addForm : function(form)
24868     {
24869        
24870         if (this.childForms.indexOf(form) > -1) {
24871             // already added..
24872             return;
24873         }
24874         this.childForms.push(form);
24875         var n = '';
24876         Roo.each(form.allItems, function (fe) {
24877             
24878             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24879             if (this.findField(n)) { // already added..
24880                 return;
24881             }
24882             var add = new Roo.form.Hidden({
24883                 name : n
24884             });
24885             add.render(this.el);
24886             
24887             this.add( add );
24888         }, this);
24889         
24890     },
24891     /**
24892      * Mark fields in this form invalid in bulk.
24893      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24894      * @return {BasicForm} this
24895      */
24896     markInvalid : function(errors){
24897         if(errors instanceof Array){
24898             for(var i = 0, len = errors.length; i < len; i++){
24899                 var fieldError = errors[i];
24900                 var f = this.findField(fieldError.id);
24901                 if(f){
24902                     f.markInvalid(fieldError.msg);
24903                 }
24904             }
24905         }else{
24906             var field, id;
24907             for(id in errors){
24908                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24909                     field.markInvalid(errors[id]);
24910                 }
24911             }
24912         }
24913         Roo.each(this.childForms || [], function (f) {
24914             f.markInvalid(errors);
24915         });
24916         
24917         return this;
24918     },
24919
24920     /**
24921      * Set values for fields in this form in bulk.
24922      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24923      * @return {BasicForm} this
24924      */
24925     setValues : function(values){
24926         if(values instanceof Array){ // array of objects
24927             for(var i = 0, len = values.length; i < len; i++){
24928                 var v = values[i];
24929                 var f = this.findField(v.id);
24930                 if(f){
24931                     f.setValue(v.value);
24932                     if(this.trackResetOnLoad){
24933                         f.originalValue = f.getValue();
24934                     }
24935                 }
24936             }
24937         }else{ // object hash
24938             var field, id;
24939             for(id in values){
24940                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24941                     
24942                     if (field.setFromData && 
24943                         field.valueField && 
24944                         field.displayField &&
24945                         // combos' with local stores can 
24946                         // be queried via setValue()
24947                         // to set their value..
24948                         (field.store && !field.store.isLocal)
24949                         ) {
24950                         // it's a combo
24951                         var sd = { };
24952                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24953                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24954                         field.setFromData(sd);
24955                         
24956                     } else {
24957                         field.setValue(values[id]);
24958                     }
24959                     
24960                     
24961                     if(this.trackResetOnLoad){
24962                         field.originalValue = field.getValue();
24963                     }
24964                 }
24965             }
24966         }
24967         this.resetHasChanged();
24968         
24969         
24970         Roo.each(this.childForms || [], function (f) {
24971             f.setValues(values);
24972             f.resetHasChanged();
24973         });
24974                 
24975         return this;
24976     },
24977  
24978     /**
24979      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24980      * they are returned as an array.
24981      * @param {Boolean} asString
24982      * @return {Object}
24983      */
24984     getValues : function(asString){
24985         if (this.childForms) {
24986             // copy values from the child forms
24987             Roo.each(this.childForms, function (f) {
24988                 this.setValues(f.getValues());
24989             }, this);
24990         }
24991         
24992         // use formdata
24993         if (typeof(FormData) != 'undefined' && asString !== true) {
24994             // this relies on a 'recent' version of chrome apparently...
24995             try {
24996                 var fd = (new FormData(this.el.dom)).entries();
24997                 var ret = {};
24998                 var ent = fd.next();
24999                 while (!ent.done) {
25000                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25001                     ent = fd.next();
25002                 };
25003                 return ret;
25004             } catch(e) {
25005                 
25006             }
25007             
25008         }
25009         
25010         
25011         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25012         if(asString === true){
25013             return fs;
25014         }
25015         return Roo.urlDecode(fs);
25016     },
25017     
25018     /**
25019      * Returns the fields in this form as an object with key/value pairs. 
25020      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25021      * @return {Object}
25022      */
25023     getFieldValues : function(with_hidden)
25024     {
25025         if (this.childForms) {
25026             // copy values from the child forms
25027             // should this call getFieldValues - probably not as we do not currently copy
25028             // hidden fields when we generate..
25029             Roo.each(this.childForms, function (f) {
25030                 this.setValues(f.getValues());
25031             }, this);
25032         }
25033         
25034         var ret = {};
25035         this.items.each(function(f){
25036             if (!f.getName()) {
25037                 return;
25038             }
25039             var v = f.getValue();
25040             if (f.inputType =='radio') {
25041                 if (typeof(ret[f.getName()]) == 'undefined') {
25042                     ret[f.getName()] = ''; // empty..
25043                 }
25044                 
25045                 if (!f.el.dom.checked) {
25046                     return;
25047                     
25048                 }
25049                 v = f.el.dom.value;
25050                 
25051             }
25052             
25053             // not sure if this supported any more..
25054             if ((typeof(v) == 'object') && f.getRawValue) {
25055                 v = f.getRawValue() ; // dates..
25056             }
25057             // combo boxes where name != hiddenName...
25058             if (f.name != f.getName()) {
25059                 ret[f.name] = f.getRawValue();
25060             }
25061             ret[f.getName()] = v;
25062         });
25063         
25064         return ret;
25065     },
25066
25067     /**
25068      * Clears all invalid messages in this form.
25069      * @return {BasicForm} this
25070      */
25071     clearInvalid : function(){
25072         this.items.each(function(f){
25073            f.clearInvalid();
25074         });
25075         
25076         Roo.each(this.childForms || [], function (f) {
25077             f.clearInvalid();
25078         });
25079         
25080         
25081         return this;
25082     },
25083
25084     /**
25085      * Resets this form.
25086      * @return {BasicForm} this
25087      */
25088     reset : function(){
25089         this.items.each(function(f){
25090             f.reset();
25091         });
25092         
25093         Roo.each(this.childForms || [], function (f) {
25094             f.reset();
25095         });
25096         this.resetHasChanged();
25097         
25098         return this;
25099     },
25100
25101     /**
25102      * Add Roo.form components to this form.
25103      * @param {Field} field1
25104      * @param {Field} field2 (optional)
25105      * @param {Field} etc (optional)
25106      * @return {BasicForm} this
25107      */
25108     add : function(){
25109         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25110         return this;
25111     },
25112
25113
25114     /**
25115      * Removes a field from the items collection (does NOT remove its markup).
25116      * @param {Field} field
25117      * @return {BasicForm} this
25118      */
25119     remove : function(field){
25120         this.items.remove(field);
25121         return this;
25122     },
25123
25124     /**
25125      * Looks at the fields in this form, checks them for an id attribute,
25126      * and calls applyTo on the existing dom element with that id.
25127      * @return {BasicForm} this
25128      */
25129     render : function(){
25130         this.items.each(function(f){
25131             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25132                 f.applyTo(f.id);
25133             }
25134         });
25135         return this;
25136     },
25137
25138     /**
25139      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25140      * @param {Object} values
25141      * @return {BasicForm} this
25142      */
25143     applyToFields : function(o){
25144         this.items.each(function(f){
25145            Roo.apply(f, o);
25146         });
25147         return this;
25148     },
25149
25150     /**
25151      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25152      * @param {Object} values
25153      * @return {BasicForm} this
25154      */
25155     applyIfToFields : function(o){
25156         this.items.each(function(f){
25157            Roo.applyIf(f, o);
25158         });
25159         return this;
25160     }
25161 });
25162
25163 // back compat
25164 Roo.BasicForm = Roo.form.BasicForm;
25165
25166 Roo.apply(Roo.form.BasicForm, {
25167     
25168     popover : {
25169         
25170         padding : 5,
25171         
25172         isApplied : false,
25173         
25174         isMasked : false,
25175         
25176         form : false,
25177         
25178         target : false,
25179         
25180         intervalID : false,
25181         
25182         maskEl : false,
25183         
25184         apply : function()
25185         {
25186             if(this.isApplied){
25187                 return;
25188             }
25189             
25190             this.maskEl = {
25191                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25192                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25193                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25194                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25195             };
25196             
25197             this.maskEl.top.enableDisplayMode("block");
25198             this.maskEl.left.enableDisplayMode("block");
25199             this.maskEl.bottom.enableDisplayMode("block");
25200             this.maskEl.right.enableDisplayMode("block");
25201             
25202             Roo.get(document.body).on('click', function(){
25203                 this.unmask();
25204             }, this);
25205             
25206             Roo.get(document.body).on('touchstart', function(){
25207                 this.unmask();
25208             }, this);
25209             
25210             this.isApplied = true
25211         },
25212         
25213         mask : function(form, target)
25214         {
25215             this.form = form;
25216             
25217             this.target = target;
25218             
25219             if(!this.form.errorMask || !target.el){
25220                 return;
25221             }
25222             
25223             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25224             
25225             var ot = this.target.el.calcOffsetsTo(scrollable);
25226             
25227             var scrollTo = ot[1] - this.form.maskOffset;
25228             
25229             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25230             
25231             scrollable.scrollTo('top', scrollTo);
25232             
25233             var el = this.target.wrap || this.target.el;
25234             
25235             var box = el.getBox();
25236             
25237             this.maskEl.top.setStyle('position', 'absolute');
25238             this.maskEl.top.setStyle('z-index', 10000);
25239             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25240             this.maskEl.top.setLeft(0);
25241             this.maskEl.top.setTop(0);
25242             this.maskEl.top.show();
25243             
25244             this.maskEl.left.setStyle('position', 'absolute');
25245             this.maskEl.left.setStyle('z-index', 10000);
25246             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25247             this.maskEl.left.setLeft(0);
25248             this.maskEl.left.setTop(box.y - this.padding);
25249             this.maskEl.left.show();
25250
25251             this.maskEl.bottom.setStyle('position', 'absolute');
25252             this.maskEl.bottom.setStyle('z-index', 10000);
25253             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25254             this.maskEl.bottom.setLeft(0);
25255             this.maskEl.bottom.setTop(box.bottom + this.padding);
25256             this.maskEl.bottom.show();
25257
25258             this.maskEl.right.setStyle('position', 'absolute');
25259             this.maskEl.right.setStyle('z-index', 10000);
25260             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25261             this.maskEl.right.setLeft(box.right + this.padding);
25262             this.maskEl.right.setTop(box.y - this.padding);
25263             this.maskEl.right.show();
25264
25265             this.intervalID = window.setInterval(function() {
25266                 Roo.form.BasicForm.popover.unmask();
25267             }, 10000);
25268
25269             window.onwheel = function(){ return false;};
25270             
25271             (function(){ this.isMasked = true; }).defer(500, this);
25272             
25273         },
25274         
25275         unmask : function()
25276         {
25277             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25278                 return;
25279             }
25280             
25281             this.maskEl.top.setStyle('position', 'absolute');
25282             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25283             this.maskEl.top.hide();
25284
25285             this.maskEl.left.setStyle('position', 'absolute');
25286             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25287             this.maskEl.left.hide();
25288
25289             this.maskEl.bottom.setStyle('position', 'absolute');
25290             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25291             this.maskEl.bottom.hide();
25292
25293             this.maskEl.right.setStyle('position', 'absolute');
25294             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25295             this.maskEl.right.hide();
25296             
25297             window.onwheel = function(){ return true;};
25298             
25299             if(this.intervalID){
25300                 window.clearInterval(this.intervalID);
25301                 this.intervalID = false;
25302             }
25303             
25304             this.isMasked = false;
25305             
25306         }
25307         
25308     }
25309     
25310 });/*
25311  * Based on:
25312  * Ext JS Library 1.1.1
25313  * Copyright(c) 2006-2007, Ext JS, LLC.
25314  *
25315  * Originally Released Under LGPL - original licence link has changed is not relivant.
25316  *
25317  * Fork - LGPL
25318  * <script type="text/javascript">
25319  */
25320
25321 /**
25322  * @class Roo.form.Form
25323  * @extends Roo.form.BasicForm
25324  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25325  * @constructor
25326  * @param {Object} config Configuration options
25327  */
25328 Roo.form.Form = function(config){
25329     var xitems =  [];
25330     if (config.items) {
25331         xitems = config.items;
25332         delete config.items;
25333     }
25334    
25335     
25336     Roo.form.Form.superclass.constructor.call(this, null, config);
25337     this.url = this.url || this.action;
25338     if(!this.root){
25339         this.root = new Roo.form.Layout(Roo.applyIf({
25340             id: Roo.id()
25341         }, config));
25342     }
25343     this.active = this.root;
25344     /**
25345      * Array of all the buttons that have been added to this form via {@link addButton}
25346      * @type Array
25347      */
25348     this.buttons = [];
25349     this.allItems = [];
25350     this.addEvents({
25351         /**
25352          * @event clientvalidation
25353          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25354          * @param {Form} this
25355          * @param {Boolean} valid true if the form has passed client-side validation
25356          */
25357         clientvalidation: true,
25358         /**
25359          * @event rendered
25360          * Fires when the form is rendered
25361          * @param {Roo.form.Form} form
25362          */
25363         rendered : true
25364     });
25365     
25366     if (this.progressUrl) {
25367             // push a hidden field onto the list of fields..
25368             this.addxtype( {
25369                     xns: Roo.form, 
25370                     xtype : 'Hidden', 
25371                     name : 'UPLOAD_IDENTIFIER' 
25372             });
25373         }
25374         
25375     
25376     Roo.each(xitems, this.addxtype, this);
25377     
25378 };
25379
25380 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25381     /**
25382      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25383      */
25384     /**
25385      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25386      */
25387     /**
25388      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25389      */
25390     buttonAlign:'center',
25391
25392     /**
25393      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25394      */
25395     minButtonWidth:75,
25396
25397     /**
25398      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25399      * This property cascades to child containers if not set.
25400      */
25401     labelAlign:'left',
25402
25403     /**
25404      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25405      * fires a looping event with that state. This is required to bind buttons to the valid
25406      * state using the config value formBind:true on the button.
25407      */
25408     monitorValid : false,
25409
25410     /**
25411      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25412      */
25413     monitorPoll : 200,
25414     
25415     /**
25416      * @cfg {String} progressUrl - Url to return progress data 
25417      */
25418     
25419     progressUrl : false,
25420     /**
25421      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25422      * sending a formdata with extra parameters - eg uploaded elements.
25423      */
25424     
25425     formData : false,
25426     
25427     /**
25428      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25429      * fields are added and the column is closed. If no fields are passed the column remains open
25430      * until end() is called.
25431      * @param {Object} config The config to pass to the column
25432      * @param {Field} field1 (optional)
25433      * @param {Field} field2 (optional)
25434      * @param {Field} etc (optional)
25435      * @return Column The column container object
25436      */
25437     column : function(c){
25438         var col = new Roo.form.Column(c);
25439         this.start(col);
25440         if(arguments.length > 1){ // duplicate code required because of Opera
25441             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25442             this.end();
25443         }
25444         return col;
25445     },
25446
25447     /**
25448      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25449      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25450      * until end() is called.
25451      * @param {Object} config The config to pass to the fieldset
25452      * @param {Field} field1 (optional)
25453      * @param {Field} field2 (optional)
25454      * @param {Field} etc (optional)
25455      * @return FieldSet The fieldset container object
25456      */
25457     fieldset : function(c){
25458         var fs = new Roo.form.FieldSet(c);
25459         this.start(fs);
25460         if(arguments.length > 1){ // duplicate code required because of Opera
25461             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25462             this.end();
25463         }
25464         return fs;
25465     },
25466
25467     /**
25468      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25469      * fields are added and the container is closed. If no fields are passed the container remains open
25470      * until end() is called.
25471      * @param {Object} config The config to pass to the Layout
25472      * @param {Field} field1 (optional)
25473      * @param {Field} field2 (optional)
25474      * @param {Field} etc (optional)
25475      * @return Layout The container object
25476      */
25477     container : function(c){
25478         var l = new Roo.form.Layout(c);
25479         this.start(l);
25480         if(arguments.length > 1){ // duplicate code required because of Opera
25481             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25482             this.end();
25483         }
25484         return l;
25485     },
25486
25487     /**
25488      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25489      * @param {Object} container A Roo.form.Layout or subclass of Layout
25490      * @return {Form} this
25491      */
25492     start : function(c){
25493         // cascade label info
25494         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25495         this.active.stack.push(c);
25496         c.ownerCt = this.active;
25497         this.active = c;
25498         return this;
25499     },
25500
25501     /**
25502      * Closes the current open container
25503      * @return {Form} this
25504      */
25505     end : function(){
25506         if(this.active == this.root){
25507             return this;
25508         }
25509         this.active = this.active.ownerCt;
25510         return this;
25511     },
25512
25513     /**
25514      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25515      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25516      * as the label of the field.
25517      * @param {Field} field1
25518      * @param {Field} field2 (optional)
25519      * @param {Field} etc. (optional)
25520      * @return {Form} this
25521      */
25522     add : function(){
25523         this.active.stack.push.apply(this.active.stack, arguments);
25524         this.allItems.push.apply(this.allItems,arguments);
25525         var r = [];
25526         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25527             if(a[i].isFormField){
25528                 r.push(a[i]);
25529             }
25530         }
25531         if(r.length > 0){
25532             Roo.form.Form.superclass.add.apply(this, r);
25533         }
25534         return this;
25535     },
25536     
25537
25538     
25539     
25540     
25541      /**
25542      * Find any element that has been added to a form, using it's ID or name
25543      * This can include framesets, columns etc. along with regular fields..
25544      * @param {String} id - id or name to find.
25545      
25546      * @return {Element} e - or false if nothing found.
25547      */
25548     findbyId : function(id)
25549     {
25550         var ret = false;
25551         if (!id) {
25552             return ret;
25553         }
25554         Roo.each(this.allItems, function(f){
25555             if (f.id == id || f.name == id ){
25556                 ret = f;
25557                 return false;
25558             }
25559         });
25560         return ret;
25561     },
25562
25563     
25564     
25565     /**
25566      * Render this form into the passed container. This should only be called once!
25567      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25568      * @return {Form} this
25569      */
25570     render : function(ct)
25571     {
25572         
25573         
25574         
25575         ct = Roo.get(ct);
25576         var o = this.autoCreate || {
25577             tag: 'form',
25578             method : this.method || 'POST',
25579             id : this.id || Roo.id()
25580         };
25581         this.initEl(ct.createChild(o));
25582
25583         this.root.render(this.el);
25584         
25585        
25586              
25587         this.items.each(function(f){
25588             f.render('x-form-el-'+f.id);
25589         });
25590
25591         if(this.buttons.length > 0){
25592             // tables are required to maintain order and for correct IE layout
25593             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25594                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25595                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25596             }}, null, true);
25597             var tr = tb.getElementsByTagName('tr')[0];
25598             for(var i = 0, len = this.buttons.length; i < len; i++) {
25599                 var b = this.buttons[i];
25600                 var td = document.createElement('td');
25601                 td.className = 'x-form-btn-td';
25602                 b.render(tr.appendChild(td));
25603             }
25604         }
25605         if(this.monitorValid){ // initialize after render
25606             this.startMonitoring();
25607         }
25608         this.fireEvent('rendered', this);
25609         return this;
25610     },
25611
25612     /**
25613      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25614      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25615      * object or a valid Roo.DomHelper element config
25616      * @param {Function} handler The function called when the button is clicked
25617      * @param {Object} scope (optional) The scope of the handler function
25618      * @return {Roo.Button}
25619      */
25620     addButton : function(config, handler, scope){
25621         var bc = {
25622             handler: handler,
25623             scope: scope,
25624             minWidth: this.minButtonWidth,
25625             hideParent:true
25626         };
25627         if(typeof config == "string"){
25628             bc.text = config;
25629         }else{
25630             Roo.apply(bc, config);
25631         }
25632         var btn = new Roo.Button(null, bc);
25633         this.buttons.push(btn);
25634         return btn;
25635     },
25636
25637      /**
25638      * Adds a series of form elements (using the xtype property as the factory method.
25639      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25640      * @param {Object} config 
25641      */
25642     
25643     addxtype : function()
25644     {
25645         var ar = Array.prototype.slice.call(arguments, 0);
25646         var ret = false;
25647         for(var i = 0; i < ar.length; i++) {
25648             if (!ar[i]) {
25649                 continue; // skip -- if this happends something invalid got sent, we 
25650                 // should ignore it, as basically that interface element will not show up
25651                 // and that should be pretty obvious!!
25652             }
25653             
25654             if (Roo.form[ar[i].xtype]) {
25655                 ar[i].form = this;
25656                 var fe = Roo.factory(ar[i], Roo.form);
25657                 if (!ret) {
25658                     ret = fe;
25659                 }
25660                 fe.form = this;
25661                 if (fe.store) {
25662                     fe.store.form = this;
25663                 }
25664                 if (fe.isLayout) {  
25665                          
25666                     this.start(fe);
25667                     this.allItems.push(fe);
25668                     if (fe.items && fe.addxtype) {
25669                         fe.addxtype.apply(fe, fe.items);
25670                         delete fe.items;
25671                     }
25672                      this.end();
25673                     continue;
25674                 }
25675                 
25676                 
25677                  
25678                 this.add(fe);
25679               //  console.log('adding ' + ar[i].xtype);
25680             }
25681             if (ar[i].xtype == 'Button') {  
25682                 //console.log('adding button');
25683                 //console.log(ar[i]);
25684                 this.addButton(ar[i]);
25685                 this.allItems.push(fe);
25686                 continue;
25687             }
25688             
25689             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25690                 alert('end is not supported on xtype any more, use items');
25691             //    this.end();
25692             //    //console.log('adding end');
25693             }
25694             
25695         }
25696         return ret;
25697     },
25698     
25699     /**
25700      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25701      * option "monitorValid"
25702      */
25703     startMonitoring : function(){
25704         if(!this.bound){
25705             this.bound = true;
25706             Roo.TaskMgr.start({
25707                 run : this.bindHandler,
25708                 interval : this.monitorPoll || 200,
25709                 scope: this
25710             });
25711         }
25712     },
25713
25714     /**
25715      * Stops monitoring of the valid state of this form
25716      */
25717     stopMonitoring : function(){
25718         this.bound = false;
25719     },
25720
25721     // private
25722     bindHandler : function(){
25723         if(!this.bound){
25724             return false; // stops binding
25725         }
25726         var valid = true;
25727         this.items.each(function(f){
25728             if(!f.isValid(true)){
25729                 valid = false;
25730                 return false;
25731             }
25732         });
25733         for(var i = 0, len = this.buttons.length; i < len; i++){
25734             var btn = this.buttons[i];
25735             if(btn.formBind === true && btn.disabled === valid){
25736                 btn.setDisabled(!valid);
25737             }
25738         }
25739         this.fireEvent('clientvalidation', this, valid);
25740     }
25741     
25742     
25743     
25744     
25745     
25746     
25747     
25748     
25749 });
25750
25751
25752 // back compat
25753 Roo.Form = Roo.form.Form;
25754 /*
25755  * Based on:
25756  * Ext JS Library 1.1.1
25757  * Copyright(c) 2006-2007, Ext JS, LLC.
25758  *
25759  * Originally Released Under LGPL - original licence link has changed is not relivant.
25760  *
25761  * Fork - LGPL
25762  * <script type="text/javascript">
25763  */
25764
25765 // as we use this in bootstrap.
25766 Roo.namespace('Roo.form');
25767  /**
25768  * @class Roo.form.Action
25769  * Internal Class used to handle form actions
25770  * @constructor
25771  * @param {Roo.form.BasicForm} el The form element or its id
25772  * @param {Object} config Configuration options
25773  */
25774
25775  
25776  
25777 // define the action interface
25778 Roo.form.Action = function(form, options){
25779     this.form = form;
25780     this.options = options || {};
25781 };
25782 /**
25783  * Client Validation Failed
25784  * @const 
25785  */
25786 Roo.form.Action.CLIENT_INVALID = 'client';
25787 /**
25788  * Server Validation Failed
25789  * @const 
25790  */
25791 Roo.form.Action.SERVER_INVALID = 'server';
25792  /**
25793  * Connect to Server Failed
25794  * @const 
25795  */
25796 Roo.form.Action.CONNECT_FAILURE = 'connect';
25797 /**
25798  * Reading Data from Server Failed
25799  * @const 
25800  */
25801 Roo.form.Action.LOAD_FAILURE = 'load';
25802
25803 Roo.form.Action.prototype = {
25804     type : 'default',
25805     failureType : undefined,
25806     response : undefined,
25807     result : undefined,
25808
25809     // interface method
25810     run : function(options){
25811
25812     },
25813
25814     // interface method
25815     success : function(response){
25816
25817     },
25818
25819     // interface method
25820     handleResponse : function(response){
25821
25822     },
25823
25824     // default connection failure
25825     failure : function(response){
25826         
25827         this.response = response;
25828         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25829         this.form.afterAction(this, false);
25830     },
25831
25832     processResponse : function(response){
25833         this.response = response;
25834         if(!response.responseText){
25835             return true;
25836         }
25837         this.result = this.handleResponse(response);
25838         return this.result;
25839     },
25840
25841     // utility functions used internally
25842     getUrl : function(appendParams){
25843         var url = this.options.url || this.form.url || this.form.el.dom.action;
25844         if(appendParams){
25845             var p = this.getParams();
25846             if(p){
25847                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25848             }
25849         }
25850         return url;
25851     },
25852
25853     getMethod : function(){
25854         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25855     },
25856
25857     getParams : function(){
25858         var bp = this.form.baseParams;
25859         var p = this.options.params;
25860         if(p){
25861             if(typeof p == "object"){
25862                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25863             }else if(typeof p == 'string' && bp){
25864                 p += '&' + Roo.urlEncode(bp);
25865             }
25866         }else if(bp){
25867             p = Roo.urlEncode(bp);
25868         }
25869         return p;
25870     },
25871
25872     createCallback : function(){
25873         return {
25874             success: this.success,
25875             failure: this.failure,
25876             scope: this,
25877             timeout: (this.form.timeout*1000),
25878             upload: this.form.fileUpload ? this.success : undefined
25879         };
25880     }
25881 };
25882
25883 Roo.form.Action.Submit = function(form, options){
25884     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25885 };
25886
25887 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25888     type : 'submit',
25889
25890     haveProgress : false,
25891     uploadComplete : false,
25892     
25893     // uploadProgress indicator.
25894     uploadProgress : function()
25895     {
25896         if (!this.form.progressUrl) {
25897             return;
25898         }
25899         
25900         if (!this.haveProgress) {
25901             Roo.MessageBox.progress("Uploading", "Uploading");
25902         }
25903         if (this.uploadComplete) {
25904            Roo.MessageBox.hide();
25905            return;
25906         }
25907         
25908         this.haveProgress = true;
25909    
25910         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25911         
25912         var c = new Roo.data.Connection();
25913         c.request({
25914             url : this.form.progressUrl,
25915             params: {
25916                 id : uid
25917             },
25918             method: 'GET',
25919             success : function(req){
25920                //console.log(data);
25921                 var rdata = false;
25922                 var edata;
25923                 try  {
25924                    rdata = Roo.decode(req.responseText)
25925                 } catch (e) {
25926                     Roo.log("Invalid data from server..");
25927                     Roo.log(edata);
25928                     return;
25929                 }
25930                 if (!rdata || !rdata.success) {
25931                     Roo.log(rdata);
25932                     Roo.MessageBox.alert(Roo.encode(rdata));
25933                     return;
25934                 }
25935                 var data = rdata.data;
25936                 
25937                 if (this.uploadComplete) {
25938                    Roo.MessageBox.hide();
25939                    return;
25940                 }
25941                    
25942                 if (data){
25943                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25944                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25945                     );
25946                 }
25947                 this.uploadProgress.defer(2000,this);
25948             },
25949        
25950             failure: function(data) {
25951                 Roo.log('progress url failed ');
25952                 Roo.log(data);
25953             },
25954             scope : this
25955         });
25956            
25957     },
25958     
25959     
25960     run : function()
25961     {
25962         // run get Values on the form, so it syncs any secondary forms.
25963         this.form.getValues();
25964         
25965         var o = this.options;
25966         var method = this.getMethod();
25967         var isPost = method == 'POST';
25968         if(o.clientValidation === false || this.form.isValid()){
25969             
25970             if (this.form.progressUrl) {
25971                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25972                     (new Date() * 1) + '' + Math.random());
25973                     
25974             } 
25975             
25976             
25977             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25978                 form:this.form.el.dom,
25979                 url:this.getUrl(!isPost),
25980                 method: method,
25981                 params:isPost ? this.getParams() : null,
25982                 isUpload: this.form.fileUpload,
25983                 formData : this.form.formData
25984             }));
25985             
25986             this.uploadProgress();
25987
25988         }else if (o.clientValidation !== false){ // client validation failed
25989             this.failureType = Roo.form.Action.CLIENT_INVALID;
25990             this.form.afterAction(this, false);
25991         }
25992     },
25993
25994     success : function(response)
25995     {
25996         this.uploadComplete= true;
25997         if (this.haveProgress) {
25998             Roo.MessageBox.hide();
25999         }
26000         
26001         
26002         var result = this.processResponse(response);
26003         if(result === true || result.success){
26004             this.form.afterAction(this, true);
26005             return;
26006         }
26007         if(result.errors){
26008             this.form.markInvalid(result.errors);
26009             this.failureType = Roo.form.Action.SERVER_INVALID;
26010         }
26011         this.form.afterAction(this, false);
26012     },
26013     failure : function(response)
26014     {
26015         this.uploadComplete= true;
26016         if (this.haveProgress) {
26017             Roo.MessageBox.hide();
26018         }
26019         
26020         this.response = response;
26021         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26022         this.form.afterAction(this, false);
26023     },
26024     
26025     handleResponse : function(response){
26026         if(this.form.errorReader){
26027             var rs = this.form.errorReader.read(response);
26028             var errors = [];
26029             if(rs.records){
26030                 for(var i = 0, len = rs.records.length; i < len; i++) {
26031                     var r = rs.records[i];
26032                     errors[i] = r.data;
26033                 }
26034             }
26035             if(errors.length < 1){
26036                 errors = null;
26037             }
26038             return {
26039                 success : rs.success,
26040                 errors : errors
26041             };
26042         }
26043         var ret = false;
26044         try {
26045             ret = Roo.decode(response.responseText);
26046         } catch (e) {
26047             ret = {
26048                 success: false,
26049                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26050                 errors : []
26051             };
26052         }
26053         return ret;
26054         
26055     }
26056 });
26057
26058
26059 Roo.form.Action.Load = function(form, options){
26060     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26061     this.reader = this.form.reader;
26062 };
26063
26064 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26065     type : 'load',
26066
26067     run : function(){
26068         
26069         Roo.Ajax.request(Roo.apply(
26070                 this.createCallback(), {
26071                     method:this.getMethod(),
26072                     url:this.getUrl(false),
26073                     params:this.getParams()
26074         }));
26075     },
26076
26077     success : function(response){
26078         
26079         var result = this.processResponse(response);
26080         if(result === true || !result.success || !result.data){
26081             this.failureType = Roo.form.Action.LOAD_FAILURE;
26082             this.form.afterAction(this, false);
26083             return;
26084         }
26085         this.form.clearInvalid();
26086         this.form.setValues(result.data);
26087         this.form.afterAction(this, true);
26088     },
26089
26090     handleResponse : function(response){
26091         if(this.form.reader){
26092             var rs = this.form.reader.read(response);
26093             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26094             return {
26095                 success : rs.success,
26096                 data : data
26097             };
26098         }
26099         return Roo.decode(response.responseText);
26100     }
26101 });
26102
26103 Roo.form.Action.ACTION_TYPES = {
26104     'load' : Roo.form.Action.Load,
26105     'submit' : Roo.form.Action.Submit
26106 };/*
26107  * Based on:
26108  * Ext JS Library 1.1.1
26109  * Copyright(c) 2006-2007, Ext JS, LLC.
26110  *
26111  * Originally Released Under LGPL - original licence link has changed is not relivant.
26112  *
26113  * Fork - LGPL
26114  * <script type="text/javascript">
26115  */
26116  
26117 /**
26118  * @class Roo.form.Layout
26119  * @extends Roo.Component
26120  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26121  * @constructor
26122  * @param {Object} config Configuration options
26123  */
26124 Roo.form.Layout = function(config){
26125     var xitems = [];
26126     if (config.items) {
26127         xitems = config.items;
26128         delete config.items;
26129     }
26130     Roo.form.Layout.superclass.constructor.call(this, config);
26131     this.stack = [];
26132     Roo.each(xitems, this.addxtype, this);
26133      
26134 };
26135
26136 Roo.extend(Roo.form.Layout, Roo.Component, {
26137     /**
26138      * @cfg {String/Object} autoCreate
26139      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26140      */
26141     /**
26142      * @cfg {String/Object/Function} style
26143      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26144      * a function which returns such a specification.
26145      */
26146     /**
26147      * @cfg {String} labelAlign
26148      * Valid values are "left," "top" and "right" (defaults to "left")
26149      */
26150     /**
26151      * @cfg {Number} labelWidth
26152      * Fixed width in pixels of all field labels (defaults to undefined)
26153      */
26154     /**
26155      * @cfg {Boolean} clear
26156      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26157      */
26158     clear : true,
26159     /**
26160      * @cfg {String} labelSeparator
26161      * The separator to use after field labels (defaults to ':')
26162      */
26163     labelSeparator : ':',
26164     /**
26165      * @cfg {Boolean} hideLabels
26166      * True to suppress the display of field labels in this layout (defaults to false)
26167      */
26168     hideLabels : false,
26169
26170     // private
26171     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26172     
26173     isLayout : true,
26174     
26175     // private
26176     onRender : function(ct, position){
26177         if(this.el){ // from markup
26178             this.el = Roo.get(this.el);
26179         }else {  // generate
26180             var cfg = this.getAutoCreate();
26181             this.el = ct.createChild(cfg, position);
26182         }
26183         if(this.style){
26184             this.el.applyStyles(this.style);
26185         }
26186         if(this.labelAlign){
26187             this.el.addClass('x-form-label-'+this.labelAlign);
26188         }
26189         if(this.hideLabels){
26190             this.labelStyle = "display:none";
26191             this.elementStyle = "padding-left:0;";
26192         }else{
26193             if(typeof this.labelWidth == 'number'){
26194                 this.labelStyle = "width:"+this.labelWidth+"px;";
26195                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26196             }
26197             if(this.labelAlign == 'top'){
26198                 this.labelStyle = "width:auto;";
26199                 this.elementStyle = "padding-left:0;";
26200             }
26201         }
26202         var stack = this.stack;
26203         var slen = stack.length;
26204         if(slen > 0){
26205             if(!this.fieldTpl){
26206                 var t = new Roo.Template(
26207                     '<div class="x-form-item {5}">',
26208                         '<label for="{0}" style="{2}">{1}{4}</label>',
26209                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26210                         '</div>',
26211                     '</div><div class="x-form-clear-left"></div>'
26212                 );
26213                 t.disableFormats = true;
26214                 t.compile();
26215                 Roo.form.Layout.prototype.fieldTpl = t;
26216             }
26217             for(var i = 0; i < slen; i++) {
26218                 if(stack[i].isFormField){
26219                     this.renderField(stack[i]);
26220                 }else{
26221                     this.renderComponent(stack[i]);
26222                 }
26223             }
26224         }
26225         if(this.clear){
26226             this.el.createChild({cls:'x-form-clear'});
26227         }
26228     },
26229
26230     // private
26231     renderField : function(f){
26232         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26233                f.id, //0
26234                f.fieldLabel, //1
26235                f.labelStyle||this.labelStyle||'', //2
26236                this.elementStyle||'', //3
26237                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26238                f.itemCls||this.itemCls||''  //5
26239        ], true).getPrevSibling());
26240     },
26241
26242     // private
26243     renderComponent : function(c){
26244         c.render(c.isLayout ? this.el : this.el.createChild());    
26245     },
26246     /**
26247      * Adds a object form elements (using the xtype property as the factory method.)
26248      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26249      * @param {Object} config 
26250      */
26251     addxtype : function(o)
26252     {
26253         // create the lement.
26254         o.form = this.form;
26255         var fe = Roo.factory(o, Roo.form);
26256         this.form.allItems.push(fe);
26257         this.stack.push(fe);
26258         
26259         if (fe.isFormField) {
26260             this.form.items.add(fe);
26261         }
26262          
26263         return fe;
26264     }
26265 });
26266
26267 /**
26268  * @class Roo.form.Column
26269  * @extends Roo.form.Layout
26270  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26271  * @constructor
26272  * @param {Object} config Configuration options
26273  */
26274 Roo.form.Column = function(config){
26275     Roo.form.Column.superclass.constructor.call(this, config);
26276 };
26277
26278 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26279     /**
26280      * @cfg {Number/String} width
26281      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26282      */
26283     /**
26284      * @cfg {String/Object} autoCreate
26285      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26286      */
26287
26288     // private
26289     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26290
26291     // private
26292     onRender : function(ct, position){
26293         Roo.form.Column.superclass.onRender.call(this, ct, position);
26294         if(this.width){
26295             this.el.setWidth(this.width);
26296         }
26297     }
26298 });
26299
26300
26301 /**
26302  * @class Roo.form.Row
26303  * @extends Roo.form.Layout
26304  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26305  * @constructor
26306  * @param {Object} config Configuration options
26307  */
26308
26309  
26310 Roo.form.Row = function(config){
26311     Roo.form.Row.superclass.constructor.call(this, config);
26312 };
26313  
26314 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26315       /**
26316      * @cfg {Number/String} width
26317      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26318      */
26319     /**
26320      * @cfg {Number/String} height
26321      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26322      */
26323     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26324     
26325     padWidth : 20,
26326     // private
26327     onRender : function(ct, position){
26328         //console.log('row render');
26329         if(!this.rowTpl){
26330             var t = new Roo.Template(
26331                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26332                     '<label for="{0}" style="{2}">{1}{4}</label>',
26333                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26334                     '</div>',
26335                 '</div>'
26336             );
26337             t.disableFormats = true;
26338             t.compile();
26339             Roo.form.Layout.prototype.rowTpl = t;
26340         }
26341         this.fieldTpl = this.rowTpl;
26342         
26343         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26344         var labelWidth = 100;
26345         
26346         if ((this.labelAlign != 'top')) {
26347             if (typeof this.labelWidth == 'number') {
26348                 labelWidth = this.labelWidth
26349             }
26350             this.padWidth =  20 + labelWidth;
26351             
26352         }
26353         
26354         Roo.form.Column.superclass.onRender.call(this, ct, position);
26355         if(this.width){
26356             this.el.setWidth(this.width);
26357         }
26358         if(this.height){
26359             this.el.setHeight(this.height);
26360         }
26361     },
26362     
26363     // private
26364     renderField : function(f){
26365         f.fieldEl = this.fieldTpl.append(this.el, [
26366                f.id, f.fieldLabel,
26367                f.labelStyle||this.labelStyle||'',
26368                this.elementStyle||'',
26369                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26370                f.itemCls||this.itemCls||'',
26371                f.width ? f.width + this.padWidth : 160 + this.padWidth
26372        ],true);
26373     }
26374 });
26375  
26376
26377 /**
26378  * @class Roo.form.FieldSet
26379  * @extends Roo.form.Layout
26380  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26381  * @constructor
26382  * @param {Object} config Configuration options
26383  */
26384 Roo.form.FieldSet = function(config){
26385     Roo.form.FieldSet.superclass.constructor.call(this, config);
26386 };
26387
26388 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26389     /**
26390      * @cfg {String} legend
26391      * The text to display as the legend for the FieldSet (defaults to '')
26392      */
26393     /**
26394      * @cfg {String/Object} autoCreate
26395      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26396      */
26397
26398     // private
26399     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26400
26401     // private
26402     onRender : function(ct, position){
26403         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26404         if(this.legend){
26405             this.setLegend(this.legend);
26406         }
26407     },
26408
26409     // private
26410     setLegend : function(text){
26411         if(this.rendered){
26412             this.el.child('legend').update(text);
26413         }
26414     }
26415 });/*
26416  * Based on:
26417  * Ext JS Library 1.1.1
26418  * Copyright(c) 2006-2007, Ext JS, LLC.
26419  *
26420  * Originally Released Under LGPL - original licence link has changed is not relivant.
26421  *
26422  * Fork - LGPL
26423  * <script type="text/javascript">
26424  */
26425 /**
26426  * @class Roo.form.VTypes
26427  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26428  * @singleton
26429  */
26430 Roo.form.VTypes = function(){
26431     // closure these in so they are only created once.
26432     var alpha = /^[a-zA-Z_]+$/;
26433     var alphanum = /^[a-zA-Z0-9_]+$/;
26434     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26435     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26436
26437     // All these messages and functions are configurable
26438     return {
26439         /**
26440          * The function used to validate email addresses
26441          * @param {String} value The email address
26442          */
26443         'email' : function(v){
26444             return email.test(v);
26445         },
26446         /**
26447          * The error text to display when the email validation function returns false
26448          * @type String
26449          */
26450         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26451         /**
26452          * The keystroke filter mask to be applied on email input
26453          * @type RegExp
26454          */
26455         'emailMask' : /[a-z0-9_\.\-@]/i,
26456
26457         /**
26458          * The function used to validate URLs
26459          * @param {String} value The URL
26460          */
26461         'url' : function(v){
26462             return url.test(v);
26463         },
26464         /**
26465          * The error text to display when the url validation function returns false
26466          * @type String
26467          */
26468         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26469         
26470         /**
26471          * The function used to validate alpha values
26472          * @param {String} value The value
26473          */
26474         'alpha' : function(v){
26475             return alpha.test(v);
26476         },
26477         /**
26478          * The error text to display when the alpha validation function returns false
26479          * @type String
26480          */
26481         'alphaText' : 'This field should only contain letters and _',
26482         /**
26483          * The keystroke filter mask to be applied on alpha input
26484          * @type RegExp
26485          */
26486         'alphaMask' : /[a-z_]/i,
26487
26488         /**
26489          * The function used to validate alphanumeric values
26490          * @param {String} value The value
26491          */
26492         'alphanum' : function(v){
26493             return alphanum.test(v);
26494         },
26495         /**
26496          * The error text to display when the alphanumeric validation function returns false
26497          * @type String
26498          */
26499         'alphanumText' : 'This field should only contain letters, numbers and _',
26500         /**
26501          * The keystroke filter mask to be applied on alphanumeric input
26502          * @type RegExp
26503          */
26504         'alphanumMask' : /[a-z0-9_]/i
26505     };
26506 }();//<script type="text/javascript">
26507
26508 /**
26509  * @class Roo.form.FCKeditor
26510  * @extends Roo.form.TextArea
26511  * Wrapper around the FCKEditor http://www.fckeditor.net
26512  * @constructor
26513  * Creates a new FCKeditor
26514  * @param {Object} config Configuration options
26515  */
26516 Roo.form.FCKeditor = function(config){
26517     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26518     this.addEvents({
26519          /**
26520          * @event editorinit
26521          * Fired when the editor is initialized - you can add extra handlers here..
26522          * @param {FCKeditor} this
26523          * @param {Object} the FCK object.
26524          */
26525         editorinit : true
26526     });
26527     
26528     
26529 };
26530 Roo.form.FCKeditor.editors = { };
26531 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26532 {
26533     //defaultAutoCreate : {
26534     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26535     //},
26536     // private
26537     /**
26538      * @cfg {Object} fck options - see fck manual for details.
26539      */
26540     fckconfig : false,
26541     
26542     /**
26543      * @cfg {Object} fck toolbar set (Basic or Default)
26544      */
26545     toolbarSet : 'Basic',
26546     /**
26547      * @cfg {Object} fck BasePath
26548      */ 
26549     basePath : '/fckeditor/',
26550     
26551     
26552     frame : false,
26553     
26554     value : '',
26555     
26556    
26557     onRender : function(ct, position)
26558     {
26559         if(!this.el){
26560             this.defaultAutoCreate = {
26561                 tag: "textarea",
26562                 style:"width:300px;height:60px;",
26563                 autocomplete: "new-password"
26564             };
26565         }
26566         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26567         /*
26568         if(this.grow){
26569             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26570             if(this.preventScrollbars){
26571                 this.el.setStyle("overflow", "hidden");
26572             }
26573             this.el.setHeight(this.growMin);
26574         }
26575         */
26576         //console.log('onrender' + this.getId() );
26577         Roo.form.FCKeditor.editors[this.getId()] = this;
26578          
26579
26580         this.replaceTextarea() ;
26581         
26582     },
26583     
26584     getEditor : function() {
26585         return this.fckEditor;
26586     },
26587     /**
26588      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26589      * @param {Mixed} value The value to set
26590      */
26591     
26592     
26593     setValue : function(value)
26594     {
26595         //console.log('setValue: ' + value);
26596         
26597         if(typeof(value) == 'undefined') { // not sure why this is happending...
26598             return;
26599         }
26600         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26601         
26602         //if(!this.el || !this.getEditor()) {
26603         //    this.value = value;
26604             //this.setValue.defer(100,this,[value]);    
26605         //    return;
26606         //} 
26607         
26608         if(!this.getEditor()) {
26609             return;
26610         }
26611         
26612         this.getEditor().SetData(value);
26613         
26614         //
26615
26616     },
26617
26618     /**
26619      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26620      * @return {Mixed} value The field value
26621      */
26622     getValue : function()
26623     {
26624         
26625         if (this.frame && this.frame.dom.style.display == 'none') {
26626             return Roo.form.FCKeditor.superclass.getValue.call(this);
26627         }
26628         
26629         if(!this.el || !this.getEditor()) {
26630            
26631            // this.getValue.defer(100,this); 
26632             return this.value;
26633         }
26634        
26635         
26636         var value=this.getEditor().GetData();
26637         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26638         return Roo.form.FCKeditor.superclass.getValue.call(this);
26639         
26640
26641     },
26642
26643     /**
26644      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26645      * @return {Mixed} value The field value
26646      */
26647     getRawValue : function()
26648     {
26649         if (this.frame && this.frame.dom.style.display == 'none') {
26650             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26651         }
26652         
26653         if(!this.el || !this.getEditor()) {
26654             //this.getRawValue.defer(100,this); 
26655             return this.value;
26656             return;
26657         }
26658         
26659         
26660         
26661         var value=this.getEditor().GetData();
26662         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26663         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26664          
26665     },
26666     
26667     setSize : function(w,h) {
26668         
26669         
26670         
26671         //if (this.frame && this.frame.dom.style.display == 'none') {
26672         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26673         //    return;
26674         //}
26675         //if(!this.el || !this.getEditor()) {
26676         //    this.setSize.defer(100,this, [w,h]); 
26677         //    return;
26678         //}
26679         
26680         
26681         
26682         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26683         
26684         this.frame.dom.setAttribute('width', w);
26685         this.frame.dom.setAttribute('height', h);
26686         this.frame.setSize(w,h);
26687         
26688     },
26689     
26690     toggleSourceEdit : function(value) {
26691         
26692       
26693          
26694         this.el.dom.style.display = value ? '' : 'none';
26695         this.frame.dom.style.display = value ?  'none' : '';
26696         
26697     },
26698     
26699     
26700     focus: function(tag)
26701     {
26702         if (this.frame.dom.style.display == 'none') {
26703             return Roo.form.FCKeditor.superclass.focus.call(this);
26704         }
26705         if(!this.el || !this.getEditor()) {
26706             this.focus.defer(100,this, [tag]); 
26707             return;
26708         }
26709         
26710         
26711         
26712         
26713         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26714         this.getEditor().Focus();
26715         if (tgs.length) {
26716             if (!this.getEditor().Selection.GetSelection()) {
26717                 this.focus.defer(100,this, [tag]); 
26718                 return;
26719             }
26720             
26721             
26722             var r = this.getEditor().EditorDocument.createRange();
26723             r.setStart(tgs[0],0);
26724             r.setEnd(tgs[0],0);
26725             this.getEditor().Selection.GetSelection().removeAllRanges();
26726             this.getEditor().Selection.GetSelection().addRange(r);
26727             this.getEditor().Focus();
26728         }
26729         
26730     },
26731     
26732     
26733     
26734     replaceTextarea : function()
26735     {
26736         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26737             return ;
26738         }
26739         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26740         //{
26741             // We must check the elements firstly using the Id and then the name.
26742         var oTextarea = document.getElementById( this.getId() );
26743         
26744         var colElementsByName = document.getElementsByName( this.getId() ) ;
26745          
26746         oTextarea.style.display = 'none' ;
26747
26748         if ( oTextarea.tabIndex ) {            
26749             this.TabIndex = oTextarea.tabIndex ;
26750         }
26751         
26752         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26753         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26754         this.frame = Roo.get(this.getId() + '___Frame')
26755     },
26756     
26757     _getConfigHtml : function()
26758     {
26759         var sConfig = '' ;
26760
26761         for ( var o in this.fckconfig ) {
26762             sConfig += sConfig.length > 0  ? '&amp;' : '';
26763             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26764         }
26765
26766         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26767     },
26768     
26769     
26770     _getIFrameHtml : function()
26771     {
26772         var sFile = 'fckeditor.html' ;
26773         /* no idea what this is about..
26774         try
26775         {
26776             if ( (/fcksource=true/i).test( window.top.location.search ) )
26777                 sFile = 'fckeditor.original.html' ;
26778         }
26779         catch (e) { 
26780         */
26781
26782         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26783         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26784         
26785         
26786         var html = '<iframe id="' + this.getId() +
26787             '___Frame" src="' + sLink +
26788             '" width="' + this.width +
26789             '" height="' + this.height + '"' +
26790             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26791             ' frameborder="0" scrolling="no"></iframe>' ;
26792
26793         return html ;
26794     },
26795     
26796     _insertHtmlBefore : function( html, element )
26797     {
26798         if ( element.insertAdjacentHTML )       {
26799             // IE
26800             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26801         } else { // Gecko
26802             var oRange = document.createRange() ;
26803             oRange.setStartBefore( element ) ;
26804             var oFragment = oRange.createContextualFragment( html );
26805             element.parentNode.insertBefore( oFragment, element ) ;
26806         }
26807     }
26808     
26809     
26810   
26811     
26812     
26813     
26814     
26815
26816 });
26817
26818 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26819
26820 function FCKeditor_OnComplete(editorInstance){
26821     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26822     f.fckEditor = editorInstance;
26823     //console.log("loaded");
26824     f.fireEvent('editorinit', f, editorInstance);
26825
26826   
26827
26828  
26829
26830
26831
26832
26833
26834
26835
26836
26837
26838
26839
26840
26841
26842
26843
26844 //<script type="text/javascript">
26845 /**
26846  * @class Roo.form.GridField
26847  * @extends Roo.form.Field
26848  * Embed a grid (or editable grid into a form)
26849  * STATUS ALPHA
26850  * 
26851  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26852  * it needs 
26853  * xgrid.store = Roo.data.Store
26854  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26855  * xgrid.store.reader = Roo.data.JsonReader 
26856  * 
26857  * 
26858  * @constructor
26859  * Creates a new GridField
26860  * @param {Object} config Configuration options
26861  */
26862 Roo.form.GridField = function(config){
26863     Roo.form.GridField.superclass.constructor.call(this, config);
26864      
26865 };
26866
26867 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26868     /**
26869      * @cfg {Number} width  - used to restrict width of grid..
26870      */
26871     width : 100,
26872     /**
26873      * @cfg {Number} height - used to restrict height of grid..
26874      */
26875     height : 50,
26876      /**
26877      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26878          * 
26879          *}
26880      */
26881     xgrid : false, 
26882     /**
26883      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26884      * {tag: "input", type: "checkbox", autocomplete: "off"})
26885      */
26886    // defaultAutoCreate : { tag: 'div' },
26887     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26888     /**
26889      * @cfg {String} addTitle Text to include for adding a title.
26890      */
26891     addTitle : false,
26892     //
26893     onResize : function(){
26894         Roo.form.Field.superclass.onResize.apply(this, arguments);
26895     },
26896
26897     initEvents : function(){
26898         // Roo.form.Checkbox.superclass.initEvents.call(this);
26899         // has no events...
26900        
26901     },
26902
26903
26904     getResizeEl : function(){
26905         return this.wrap;
26906     },
26907
26908     getPositionEl : function(){
26909         return this.wrap;
26910     },
26911
26912     // private
26913     onRender : function(ct, position){
26914         
26915         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26916         var style = this.style;
26917         delete this.style;
26918         
26919         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26920         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26921         this.viewEl = this.wrap.createChild({ tag: 'div' });
26922         if (style) {
26923             this.viewEl.applyStyles(style);
26924         }
26925         if (this.width) {
26926             this.viewEl.setWidth(this.width);
26927         }
26928         if (this.height) {
26929             this.viewEl.setHeight(this.height);
26930         }
26931         //if(this.inputValue !== undefined){
26932         //this.setValue(this.value);
26933         
26934         
26935         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26936         
26937         
26938         this.grid.render();
26939         this.grid.getDataSource().on('remove', this.refreshValue, this);
26940         this.grid.getDataSource().on('update', this.refreshValue, this);
26941         this.grid.on('afteredit', this.refreshValue, this);
26942  
26943     },
26944      
26945     
26946     /**
26947      * Sets the value of the item. 
26948      * @param {String} either an object  or a string..
26949      */
26950     setValue : function(v){
26951         //this.value = v;
26952         v = v || []; // empty set..
26953         // this does not seem smart - it really only affects memoryproxy grids..
26954         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26955             var ds = this.grid.getDataSource();
26956             // assumes a json reader..
26957             var data = {}
26958             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26959             ds.loadData( data);
26960         }
26961         // clear selection so it does not get stale.
26962         if (this.grid.sm) { 
26963             this.grid.sm.clearSelections();
26964         }
26965         
26966         Roo.form.GridField.superclass.setValue.call(this, v);
26967         this.refreshValue();
26968         // should load data in the grid really....
26969     },
26970     
26971     // private
26972     refreshValue: function() {
26973          var val = [];
26974         this.grid.getDataSource().each(function(r) {
26975             val.push(r.data);
26976         });
26977         this.el.dom.value = Roo.encode(val);
26978     }
26979     
26980      
26981     
26982     
26983 });/*
26984  * Based on:
26985  * Ext JS Library 1.1.1
26986  * Copyright(c) 2006-2007, Ext JS, LLC.
26987  *
26988  * Originally Released Under LGPL - original licence link has changed is not relivant.
26989  *
26990  * Fork - LGPL
26991  * <script type="text/javascript">
26992  */
26993 /**
26994  * @class Roo.form.DisplayField
26995  * @extends Roo.form.Field
26996  * A generic Field to display non-editable data.
26997  * @cfg {Boolean} closable (true|false) default false
26998  * @constructor
26999  * Creates a new Display Field item.
27000  * @param {Object} config Configuration options
27001  */
27002 Roo.form.DisplayField = function(config){
27003     Roo.form.DisplayField.superclass.constructor.call(this, config);
27004     
27005     this.addEvents({
27006         /**
27007          * @event close
27008          * Fires after the click the close btn
27009              * @param {Roo.form.DisplayField} this
27010              */
27011         close : true
27012     });
27013 };
27014
27015 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27016     inputType:      'hidden',
27017     allowBlank:     true,
27018     readOnly:         true,
27019     
27020  
27021     /**
27022      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27023      */
27024     focusClass : undefined,
27025     /**
27026      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27027      */
27028     fieldClass: 'x-form-field',
27029     
27030      /**
27031      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27032      */
27033     valueRenderer: undefined,
27034     
27035     width: 100,
27036     /**
27037      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27038      * {tag: "input", type: "checkbox", autocomplete: "off"})
27039      */
27040      
27041  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27042  
27043     closable : false,
27044     
27045     onResize : function(){
27046         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27047         
27048     },
27049
27050     initEvents : function(){
27051         // Roo.form.Checkbox.superclass.initEvents.call(this);
27052         // has no events...
27053         
27054         if(this.closable){
27055             this.closeEl.on('click', this.onClose, this);
27056         }
27057        
27058     },
27059
27060
27061     getResizeEl : function(){
27062         return this.wrap;
27063     },
27064
27065     getPositionEl : function(){
27066         return this.wrap;
27067     },
27068
27069     // private
27070     onRender : function(ct, position){
27071         
27072         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27073         //if(this.inputValue !== undefined){
27074         this.wrap = this.el.wrap();
27075         
27076         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27077         
27078         if(this.closable){
27079             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27080         }
27081         
27082         if (this.bodyStyle) {
27083             this.viewEl.applyStyles(this.bodyStyle);
27084         }
27085         //this.viewEl.setStyle('padding', '2px');
27086         
27087         this.setValue(this.value);
27088         
27089     },
27090 /*
27091     // private
27092     initValue : Roo.emptyFn,
27093
27094   */
27095
27096         // private
27097     onClick : function(){
27098         
27099     },
27100
27101     /**
27102      * Sets the checked state of the checkbox.
27103      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27104      */
27105     setValue : function(v){
27106         this.value = v;
27107         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27108         // this might be called before we have a dom element..
27109         if (!this.viewEl) {
27110             return;
27111         }
27112         this.viewEl.dom.innerHTML = html;
27113         Roo.form.DisplayField.superclass.setValue.call(this, v);
27114
27115     },
27116     
27117     onClose : function(e)
27118     {
27119         e.preventDefault();
27120         
27121         this.fireEvent('close', this);
27122     }
27123 });/*
27124  * 
27125  * Licence- LGPL
27126  * 
27127  */
27128
27129 /**
27130  * @class Roo.form.DayPicker
27131  * @extends Roo.form.Field
27132  * A Day picker show [M] [T] [W] ....
27133  * @constructor
27134  * Creates a new Day Picker
27135  * @param {Object} config Configuration options
27136  */
27137 Roo.form.DayPicker= function(config){
27138     Roo.form.DayPicker.superclass.constructor.call(this, config);
27139      
27140 };
27141
27142 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27143     /**
27144      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27145      */
27146     focusClass : undefined,
27147     /**
27148      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27149      */
27150     fieldClass: "x-form-field",
27151    
27152     /**
27153      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27154      * {tag: "input", type: "checkbox", autocomplete: "off"})
27155      */
27156     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27157     
27158    
27159     actionMode : 'viewEl', 
27160     //
27161     // private
27162  
27163     inputType : 'hidden',
27164     
27165      
27166     inputElement: false, // real input element?
27167     basedOn: false, // ????
27168     
27169     isFormField: true, // not sure where this is needed!!!!
27170
27171     onResize : function(){
27172         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27173         if(!this.boxLabel){
27174             this.el.alignTo(this.wrap, 'c-c');
27175         }
27176     },
27177
27178     initEvents : function(){
27179         Roo.form.Checkbox.superclass.initEvents.call(this);
27180         this.el.on("click", this.onClick,  this);
27181         this.el.on("change", this.onClick,  this);
27182     },
27183
27184
27185     getResizeEl : function(){
27186         return this.wrap;
27187     },
27188
27189     getPositionEl : function(){
27190         return this.wrap;
27191     },
27192
27193     
27194     // private
27195     onRender : function(ct, position){
27196         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27197        
27198         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27199         
27200         var r1 = '<table><tr>';
27201         var r2 = '<tr class="x-form-daypick-icons">';
27202         for (var i=0; i < 7; i++) {
27203             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27204             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27205         }
27206         
27207         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27208         viewEl.select('img').on('click', this.onClick, this);
27209         this.viewEl = viewEl;   
27210         
27211         
27212         // this will not work on Chrome!!!
27213         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27214         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27215         
27216         
27217           
27218
27219     },
27220
27221     // private
27222     initValue : Roo.emptyFn,
27223
27224     /**
27225      * Returns the checked state of the checkbox.
27226      * @return {Boolean} True if checked, else false
27227      */
27228     getValue : function(){
27229         return this.el.dom.value;
27230         
27231     },
27232
27233         // private
27234     onClick : function(e){ 
27235         //this.setChecked(!this.checked);
27236         Roo.get(e.target).toggleClass('x-menu-item-checked');
27237         this.refreshValue();
27238         //if(this.el.dom.checked != this.checked){
27239         //    this.setValue(this.el.dom.checked);
27240        // }
27241     },
27242     
27243     // private
27244     refreshValue : function()
27245     {
27246         var val = '';
27247         this.viewEl.select('img',true).each(function(e,i,n)  {
27248             val += e.is(".x-menu-item-checked") ? String(n) : '';
27249         });
27250         this.setValue(val, true);
27251     },
27252
27253     /**
27254      * Sets the checked state of the checkbox.
27255      * On is always based on a string comparison between inputValue and the param.
27256      * @param {Boolean/String} value - the value to set 
27257      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27258      */
27259     setValue : function(v,suppressEvent){
27260         if (!this.el.dom) {
27261             return;
27262         }
27263         var old = this.el.dom.value ;
27264         this.el.dom.value = v;
27265         if (suppressEvent) {
27266             return ;
27267         }
27268          
27269         // update display..
27270         this.viewEl.select('img',true).each(function(e,i,n)  {
27271             
27272             var on = e.is(".x-menu-item-checked");
27273             var newv = v.indexOf(String(n)) > -1;
27274             if (on != newv) {
27275                 e.toggleClass('x-menu-item-checked');
27276             }
27277             
27278         });
27279         
27280         
27281         this.fireEvent('change', this, v, old);
27282         
27283         
27284     },
27285    
27286     // handle setting of hidden value by some other method!!?!?
27287     setFromHidden: function()
27288     {
27289         if(!this.el){
27290             return;
27291         }
27292         //console.log("SET FROM HIDDEN");
27293         //alert('setFrom hidden');
27294         this.setValue(this.el.dom.value);
27295     },
27296     
27297     onDestroy : function()
27298     {
27299         if(this.viewEl){
27300             Roo.get(this.viewEl).remove();
27301         }
27302          
27303         Roo.form.DayPicker.superclass.onDestroy.call(this);
27304     }
27305
27306 });/*
27307  * RooJS Library 1.1.1
27308  * Copyright(c) 2008-2011  Alan Knowles
27309  *
27310  * License - LGPL
27311  */
27312  
27313
27314 /**
27315  * @class Roo.form.ComboCheck
27316  * @extends Roo.form.ComboBox
27317  * A combobox for multiple select items.
27318  *
27319  * FIXME - could do with a reset button..
27320  * 
27321  * @constructor
27322  * Create a new ComboCheck
27323  * @param {Object} config Configuration options
27324  */
27325 Roo.form.ComboCheck = function(config){
27326     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27327     // should verify some data...
27328     // like
27329     // hiddenName = required..
27330     // displayField = required
27331     // valudField == required
27332     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27333     var _t = this;
27334     Roo.each(req, function(e) {
27335         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27336             throw "Roo.form.ComboCheck : missing value for: " + e;
27337         }
27338     });
27339     
27340     
27341 };
27342
27343 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27344      
27345      
27346     editable : false,
27347      
27348     selectedClass: 'x-menu-item-checked', 
27349     
27350     // private
27351     onRender : function(ct, position){
27352         var _t = this;
27353         
27354         
27355         
27356         if(!this.tpl){
27357             var cls = 'x-combo-list';
27358
27359             
27360             this.tpl =  new Roo.Template({
27361                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27362                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27363                    '<span>{' + this.displayField + '}</span>' +
27364                     '</div>' 
27365                 
27366             });
27367         }
27368  
27369         
27370         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27371         this.view.singleSelect = false;
27372         this.view.multiSelect = true;
27373         this.view.toggleSelect = true;
27374         this.pageTb.add(new Roo.Toolbar.Fill(), {
27375             
27376             text: 'Done',
27377             handler: function()
27378             {
27379                 _t.collapse();
27380             }
27381         });
27382     },
27383     
27384     onViewOver : function(e, t){
27385         // do nothing...
27386         return;
27387         
27388     },
27389     
27390     onViewClick : function(doFocus,index){
27391         return;
27392         
27393     },
27394     select: function () {
27395         //Roo.log("SELECT CALLED");
27396     },
27397      
27398     selectByValue : function(xv, scrollIntoView){
27399         var ar = this.getValueArray();
27400         var sels = [];
27401         
27402         Roo.each(ar, function(v) {
27403             if(v === undefined || v === null){
27404                 return;
27405             }
27406             var r = this.findRecord(this.valueField, v);
27407             if(r){
27408                 sels.push(this.store.indexOf(r))
27409                 
27410             }
27411         },this);
27412         this.view.select(sels);
27413         return false;
27414     },
27415     
27416     
27417     
27418     onSelect : function(record, index){
27419        // Roo.log("onselect Called");
27420        // this is only called by the clear button now..
27421         this.view.clearSelections();
27422         this.setValue('[]');
27423         if (this.value != this.valueBefore) {
27424             this.fireEvent('change', this, this.value, this.valueBefore);
27425             this.valueBefore = this.value;
27426         }
27427     },
27428     getValueArray : function()
27429     {
27430         var ar = [] ;
27431         
27432         try {
27433             //Roo.log(this.value);
27434             if (typeof(this.value) == 'undefined') {
27435                 return [];
27436             }
27437             var ar = Roo.decode(this.value);
27438             return  ar instanceof Array ? ar : []; //?? valid?
27439             
27440         } catch(e) {
27441             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27442             return [];
27443         }
27444          
27445     },
27446     expand : function ()
27447     {
27448         
27449         Roo.form.ComboCheck.superclass.expand.call(this);
27450         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27451         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27452         
27453
27454     },
27455     
27456     collapse : function(){
27457         Roo.form.ComboCheck.superclass.collapse.call(this);
27458         var sl = this.view.getSelectedIndexes();
27459         var st = this.store;
27460         var nv = [];
27461         var tv = [];
27462         var r;
27463         Roo.each(sl, function(i) {
27464             r = st.getAt(i);
27465             nv.push(r.get(this.valueField));
27466         },this);
27467         this.setValue(Roo.encode(nv));
27468         if (this.value != this.valueBefore) {
27469
27470             this.fireEvent('change', this, this.value, this.valueBefore);
27471             this.valueBefore = this.value;
27472         }
27473         
27474     },
27475     
27476     setValue : function(v){
27477         // Roo.log(v);
27478         this.value = v;
27479         
27480         var vals = this.getValueArray();
27481         var tv = [];
27482         Roo.each(vals, function(k) {
27483             var r = this.findRecord(this.valueField, k);
27484             if(r){
27485                 tv.push(r.data[this.displayField]);
27486             }else if(this.valueNotFoundText !== undefined){
27487                 tv.push( this.valueNotFoundText );
27488             }
27489         },this);
27490        // Roo.log(tv);
27491         
27492         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27493         this.hiddenField.value = v;
27494         this.value = v;
27495     }
27496     
27497 });/*
27498  * Based on:
27499  * Ext JS Library 1.1.1
27500  * Copyright(c) 2006-2007, Ext JS, LLC.
27501  *
27502  * Originally Released Under LGPL - original licence link has changed is not relivant.
27503  *
27504  * Fork - LGPL
27505  * <script type="text/javascript">
27506  */
27507  
27508 /**
27509  * @class Roo.form.Signature
27510  * @extends Roo.form.Field
27511  * Signature field.  
27512  * @constructor
27513  * 
27514  * @param {Object} config Configuration options
27515  */
27516
27517 Roo.form.Signature = function(config){
27518     Roo.form.Signature.superclass.constructor.call(this, config);
27519     
27520     this.addEvents({// not in used??
27521          /**
27522          * @event confirm
27523          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27524              * @param {Roo.form.Signature} combo This combo box
27525              */
27526         'confirm' : true,
27527         /**
27528          * @event reset
27529          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27530              * @param {Roo.form.ComboBox} combo This combo box
27531              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27532              */
27533         'reset' : true
27534     });
27535 };
27536
27537 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27538     /**
27539      * @cfg {Object} labels Label to use when rendering a form.
27540      * defaults to 
27541      * labels : { 
27542      *      clear : "Clear",
27543      *      confirm : "Confirm"
27544      *  }
27545      */
27546     labels : { 
27547         clear : "Clear",
27548         confirm : "Confirm"
27549     },
27550     /**
27551      * @cfg {Number} width The signature panel width (defaults to 300)
27552      */
27553     width: 300,
27554     /**
27555      * @cfg {Number} height The signature panel height (defaults to 100)
27556      */
27557     height : 100,
27558     /**
27559      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27560      */
27561     allowBlank : false,
27562     
27563     //private
27564     // {Object} signPanel The signature SVG panel element (defaults to {})
27565     signPanel : {},
27566     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27567     isMouseDown : false,
27568     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27569     isConfirmed : false,
27570     // {String} signatureTmp SVG mapping string (defaults to empty string)
27571     signatureTmp : '',
27572     
27573     
27574     defaultAutoCreate : { // modified by initCompnoent..
27575         tag: "input",
27576         type:"hidden"
27577     },
27578
27579     // private
27580     onRender : function(ct, position){
27581         
27582         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27583         
27584         this.wrap = this.el.wrap({
27585             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27586         });
27587         
27588         this.createToolbar(this);
27589         this.signPanel = this.wrap.createChild({
27590                 tag: 'div',
27591                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27592             }, this.el
27593         );
27594             
27595         this.svgID = Roo.id();
27596         this.svgEl = this.signPanel.createChild({
27597               xmlns : 'http://www.w3.org/2000/svg',
27598               tag : 'svg',
27599               id : this.svgID + "-svg",
27600               width: this.width,
27601               height: this.height,
27602               viewBox: '0 0 '+this.width+' '+this.height,
27603               cn : [
27604                 {
27605                     tag: "rect",
27606                     id: this.svgID + "-svg-r",
27607                     width: this.width,
27608                     height: this.height,
27609                     fill: "#ffa"
27610                 },
27611                 {
27612                     tag: "line",
27613                     id: this.svgID + "-svg-l",
27614                     x1: "0", // start
27615                     y1: (this.height*0.8), // start set the line in 80% of height
27616                     x2: this.width, // end
27617                     y2: (this.height*0.8), // end set the line in 80% of height
27618                     'stroke': "#666",
27619                     'stroke-width': "1",
27620                     'stroke-dasharray': "3",
27621                     'shape-rendering': "crispEdges",
27622                     'pointer-events': "none"
27623                 },
27624                 {
27625                     tag: "path",
27626                     id: this.svgID + "-svg-p",
27627                     'stroke': "navy",
27628                     'stroke-width': "3",
27629                     'fill': "none",
27630                     'pointer-events': 'none'
27631                 }
27632               ]
27633         });
27634         this.createSVG();
27635         this.svgBox = this.svgEl.dom.getScreenCTM();
27636     },
27637     createSVG : function(){ 
27638         var svg = this.signPanel;
27639         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27640         var t = this;
27641
27642         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27643         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27644         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27645         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27646         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27647         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27648         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27649         
27650     },
27651     isTouchEvent : function(e){
27652         return e.type.match(/^touch/);
27653     },
27654     getCoords : function (e) {
27655         var pt    = this.svgEl.dom.createSVGPoint();
27656         pt.x = e.clientX; 
27657         pt.y = e.clientY;
27658         if (this.isTouchEvent(e)) {
27659             pt.x =  e.targetTouches[0].clientX;
27660             pt.y = e.targetTouches[0].clientY;
27661         }
27662         var a = this.svgEl.dom.getScreenCTM();
27663         var b = a.inverse();
27664         var mx = pt.matrixTransform(b);
27665         return mx.x + ',' + mx.y;
27666     },
27667     //mouse event headler 
27668     down : function (e) {
27669         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27670         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27671         
27672         this.isMouseDown = true;
27673         
27674         e.preventDefault();
27675     },
27676     move : function (e) {
27677         if (this.isMouseDown) {
27678             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27679             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27680         }
27681         
27682         e.preventDefault();
27683     },
27684     up : function (e) {
27685         this.isMouseDown = false;
27686         var sp = this.signatureTmp.split(' ');
27687         
27688         if(sp.length > 1){
27689             if(!sp[sp.length-2].match(/^L/)){
27690                 sp.pop();
27691                 sp.pop();
27692                 sp.push("");
27693                 this.signatureTmp = sp.join(" ");
27694             }
27695         }
27696         if(this.getValue() != this.signatureTmp){
27697             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27698             this.isConfirmed = false;
27699         }
27700         e.preventDefault();
27701     },
27702     
27703     /**
27704      * Protected method that will not generally be called directly. It
27705      * is called when the editor creates its toolbar. Override this method if you need to
27706      * add custom toolbar buttons.
27707      * @param {HtmlEditor} editor
27708      */
27709     createToolbar : function(editor){
27710          function btn(id, toggle, handler){
27711             var xid = fid + '-'+ id ;
27712             return {
27713                 id : xid,
27714                 cmd : id,
27715                 cls : 'x-btn-icon x-edit-'+id,
27716                 enableToggle:toggle !== false,
27717                 scope: editor, // was editor...
27718                 handler:handler||editor.relayBtnCmd,
27719                 clickEvent:'mousedown',
27720                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27721                 tabIndex:-1
27722             };
27723         }
27724         
27725         
27726         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27727         this.tb = tb;
27728         this.tb.add(
27729            {
27730                 cls : ' x-signature-btn x-signature-'+id,
27731                 scope: editor, // was editor...
27732                 handler: this.reset,
27733                 clickEvent:'mousedown',
27734                 text: this.labels.clear
27735             },
27736             {
27737                  xtype : 'Fill',
27738                  xns: Roo.Toolbar
27739             }, 
27740             {
27741                 cls : '  x-signature-btn x-signature-'+id,
27742                 scope: editor, // was editor...
27743                 handler: this.confirmHandler,
27744                 clickEvent:'mousedown',
27745                 text: this.labels.confirm
27746             }
27747         );
27748     
27749     },
27750     //public
27751     /**
27752      * when user is clicked confirm then show this image.....
27753      * 
27754      * @return {String} Image Data URI
27755      */
27756     getImageDataURI : function(){
27757         var svg = this.svgEl.dom.parentNode.innerHTML;
27758         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27759         return src; 
27760     },
27761     /**
27762      * 
27763      * @return {Boolean} this.isConfirmed
27764      */
27765     getConfirmed : function(){
27766         return this.isConfirmed;
27767     },
27768     /**
27769      * 
27770      * @return {Number} this.width
27771      */
27772     getWidth : function(){
27773         return this.width;
27774     },
27775     /**
27776      * 
27777      * @return {Number} this.height
27778      */
27779     getHeight : function(){
27780         return this.height;
27781     },
27782     // private
27783     getSignature : function(){
27784         return this.signatureTmp;
27785     },
27786     // private
27787     reset : function(){
27788         this.signatureTmp = '';
27789         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27790         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27791         this.isConfirmed = false;
27792         Roo.form.Signature.superclass.reset.call(this);
27793     },
27794     setSignature : function(s){
27795         this.signatureTmp = s;
27796         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27797         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27798         this.setValue(s);
27799         this.isConfirmed = false;
27800         Roo.form.Signature.superclass.reset.call(this);
27801     }, 
27802     test : function(){
27803 //        Roo.log(this.signPanel.dom.contentWindow.up())
27804     },
27805     //private
27806     setConfirmed : function(){
27807         
27808         
27809         
27810 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27811     },
27812     // private
27813     confirmHandler : function(){
27814         if(!this.getSignature()){
27815             return;
27816         }
27817         
27818         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27819         this.setValue(this.getSignature());
27820         this.isConfirmed = true;
27821         
27822         this.fireEvent('confirm', this);
27823     },
27824     // private
27825     // Subclasses should provide the validation implementation by overriding this
27826     validateValue : function(value){
27827         if(this.allowBlank){
27828             return true;
27829         }
27830         
27831         if(this.isConfirmed){
27832             return true;
27833         }
27834         return false;
27835     }
27836 });/*
27837  * Based on:
27838  * Ext JS Library 1.1.1
27839  * Copyright(c) 2006-2007, Ext JS, LLC.
27840  *
27841  * Originally Released Under LGPL - original licence link has changed is not relivant.
27842  *
27843  * Fork - LGPL
27844  * <script type="text/javascript">
27845  */
27846  
27847
27848 /**
27849  * @class Roo.form.ComboBox
27850  * @extends Roo.form.TriggerField
27851  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27852  * @constructor
27853  * Create a new ComboBox.
27854  * @param {Object} config Configuration options
27855  */
27856 Roo.form.Select = function(config){
27857     Roo.form.Select.superclass.constructor.call(this, config);
27858      
27859 };
27860
27861 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27862     /**
27863      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27864      */
27865     /**
27866      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27867      * rendering into an Roo.Editor, defaults to false)
27868      */
27869     /**
27870      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27871      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27872      */
27873     /**
27874      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27875      */
27876     /**
27877      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27878      * the dropdown list (defaults to undefined, with no header element)
27879      */
27880
27881      /**
27882      * @cfg {String/Roo.Template} tpl The template to use to render the output
27883      */
27884      
27885     // private
27886     defaultAutoCreate : {tag: "select"  },
27887     /**
27888      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27889      */
27890     listWidth: undefined,
27891     /**
27892      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27893      * mode = 'remote' or 'text' if mode = 'local')
27894      */
27895     displayField: undefined,
27896     /**
27897      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27898      * mode = 'remote' or 'value' if mode = 'local'). 
27899      * Note: use of a valueField requires the user make a selection
27900      * in order for a value to be mapped.
27901      */
27902     valueField: undefined,
27903     
27904     
27905     /**
27906      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27907      * field's data value (defaults to the underlying DOM element's name)
27908      */
27909     hiddenName: undefined,
27910     /**
27911      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27912      */
27913     listClass: '',
27914     /**
27915      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27916      */
27917     selectedClass: 'x-combo-selected',
27918     /**
27919      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27920      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27921      * which displays a downward arrow icon).
27922      */
27923     triggerClass : 'x-form-arrow-trigger',
27924     /**
27925      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27926      */
27927     shadow:'sides',
27928     /**
27929      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27930      * anchor positions (defaults to 'tl-bl')
27931      */
27932     listAlign: 'tl-bl?',
27933     /**
27934      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27935      */
27936     maxHeight: 300,
27937     /**
27938      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27939      * query specified by the allQuery config option (defaults to 'query')
27940      */
27941     triggerAction: 'query',
27942     /**
27943      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27944      * (defaults to 4, does not apply if editable = false)
27945      */
27946     minChars : 4,
27947     /**
27948      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27949      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27950      */
27951     typeAhead: false,
27952     /**
27953      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27954      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27955      */
27956     queryDelay: 500,
27957     /**
27958      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27959      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27960      */
27961     pageSize: 0,
27962     /**
27963      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27964      * when editable = true (defaults to false)
27965      */
27966     selectOnFocus:false,
27967     /**
27968      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27969      */
27970     queryParam: 'query',
27971     /**
27972      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27973      * when mode = 'remote' (defaults to 'Loading...')
27974      */
27975     loadingText: 'Loading...',
27976     /**
27977      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27978      */
27979     resizable: false,
27980     /**
27981      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27982      */
27983     handleHeight : 8,
27984     /**
27985      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27986      * traditional select (defaults to true)
27987      */
27988     editable: true,
27989     /**
27990      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27991      */
27992     allQuery: '',
27993     /**
27994      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27995      */
27996     mode: 'remote',
27997     /**
27998      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27999      * listWidth has a higher value)
28000      */
28001     minListWidth : 70,
28002     /**
28003      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28004      * allow the user to set arbitrary text into the field (defaults to false)
28005      */
28006     forceSelection:false,
28007     /**
28008      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28009      * if typeAhead = true (defaults to 250)
28010      */
28011     typeAheadDelay : 250,
28012     /**
28013      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28014      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28015      */
28016     valueNotFoundText : undefined,
28017     
28018     /**
28019      * @cfg {String} defaultValue The value displayed after loading the store.
28020      */
28021     defaultValue: '',
28022     
28023     /**
28024      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28025      */
28026     blockFocus : false,
28027     
28028     /**
28029      * @cfg {Boolean} disableClear Disable showing of clear button.
28030      */
28031     disableClear : false,
28032     /**
28033      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28034      */
28035     alwaysQuery : false,
28036     
28037     //private
28038     addicon : false,
28039     editicon: false,
28040     
28041     // element that contains real text value.. (when hidden is used..)
28042      
28043     // private
28044     onRender : function(ct, position){
28045         Roo.form.Field.prototype.onRender.call(this, ct, position);
28046         
28047         if(this.store){
28048             this.store.on('beforeload', this.onBeforeLoad, this);
28049             this.store.on('load', this.onLoad, this);
28050             this.store.on('loadexception', this.onLoadException, this);
28051             this.store.load({});
28052         }
28053         
28054         
28055         
28056     },
28057
28058     // private
28059     initEvents : function(){
28060         //Roo.form.ComboBox.superclass.initEvents.call(this);
28061  
28062     },
28063
28064     onDestroy : function(){
28065        
28066         if(this.store){
28067             this.store.un('beforeload', this.onBeforeLoad, this);
28068             this.store.un('load', this.onLoad, this);
28069             this.store.un('loadexception', this.onLoadException, this);
28070         }
28071         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28072     },
28073
28074     // private
28075     fireKey : function(e){
28076         if(e.isNavKeyPress() && !this.list.isVisible()){
28077             this.fireEvent("specialkey", this, e);
28078         }
28079     },
28080
28081     // private
28082     onResize: function(w, h){
28083         
28084         return; 
28085     
28086         
28087     },
28088
28089     /**
28090      * Allow or prevent the user from directly editing the field text.  If false is passed,
28091      * the user will only be able to select from the items defined in the dropdown list.  This method
28092      * is the runtime equivalent of setting the 'editable' config option at config time.
28093      * @param {Boolean} value True to allow the user to directly edit the field text
28094      */
28095     setEditable : function(value){
28096          
28097     },
28098
28099     // private
28100     onBeforeLoad : function(){
28101         
28102         Roo.log("Select before load");
28103         return;
28104     
28105         this.innerList.update(this.loadingText ?
28106                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28107         //this.restrictHeight();
28108         this.selectedIndex = -1;
28109     },
28110
28111     // private
28112     onLoad : function(){
28113
28114     
28115         var dom = this.el.dom;
28116         dom.innerHTML = '';
28117          var od = dom.ownerDocument;
28118          
28119         if (this.emptyText) {
28120             var op = od.createElement('option');
28121             op.setAttribute('value', '');
28122             op.innerHTML = String.format('{0}', this.emptyText);
28123             dom.appendChild(op);
28124         }
28125         if(this.store.getCount() > 0){
28126            
28127             var vf = this.valueField;
28128             var df = this.displayField;
28129             this.store.data.each(function(r) {
28130                 // which colmsn to use... testing - cdoe / title..
28131                 var op = od.createElement('option');
28132                 op.setAttribute('value', r.data[vf]);
28133                 op.innerHTML = String.format('{0}', r.data[df]);
28134                 dom.appendChild(op);
28135             });
28136             if (typeof(this.defaultValue != 'undefined')) {
28137                 this.setValue(this.defaultValue);
28138             }
28139             
28140              
28141         }else{
28142             //this.onEmptyResults();
28143         }
28144         //this.el.focus();
28145     },
28146     // private
28147     onLoadException : function()
28148     {
28149         dom.innerHTML = '';
28150             
28151         Roo.log("Select on load exception");
28152         return;
28153     
28154         this.collapse();
28155         Roo.log(this.store.reader.jsonData);
28156         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28157             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28158         }
28159         
28160         
28161     },
28162     // private
28163     onTypeAhead : function(){
28164          
28165     },
28166
28167     // private
28168     onSelect : function(record, index){
28169         Roo.log('on select?');
28170         return;
28171         if(this.fireEvent('beforeselect', this, record, index) !== false){
28172             this.setFromData(index > -1 ? record.data : false);
28173             this.collapse();
28174             this.fireEvent('select', this, record, index);
28175         }
28176     },
28177
28178     /**
28179      * Returns the currently selected field value or empty string if no value is set.
28180      * @return {String} value The selected value
28181      */
28182     getValue : function(){
28183         var dom = this.el.dom;
28184         this.value = dom.options[dom.selectedIndex].value;
28185         return this.value;
28186         
28187     },
28188
28189     /**
28190      * Clears any text/value currently set in the field
28191      */
28192     clearValue : function(){
28193         this.value = '';
28194         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28195         
28196     },
28197
28198     /**
28199      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28200      * will be displayed in the field.  If the value does not match the data value of an existing item,
28201      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28202      * Otherwise the field will be blank (although the value will still be set).
28203      * @param {String} value The value to match
28204      */
28205     setValue : function(v){
28206         var d = this.el.dom;
28207         for (var i =0; i < d.options.length;i++) {
28208             if (v == d.options[i].value) {
28209                 d.selectedIndex = i;
28210                 this.value = v;
28211                 return;
28212             }
28213         }
28214         this.clearValue();
28215     },
28216     /**
28217      * @property {Object} the last set data for the element
28218      */
28219     
28220     lastData : false,
28221     /**
28222      * Sets the value of the field based on a object which is related to the record format for the store.
28223      * @param {Object} value the value to set as. or false on reset?
28224      */
28225     setFromData : function(o){
28226         Roo.log('setfrom data?');
28227          
28228         
28229         
28230     },
28231     // private
28232     reset : function(){
28233         this.clearValue();
28234     },
28235     // private
28236     findRecord : function(prop, value){
28237         
28238         return false;
28239     
28240         var record;
28241         if(this.store.getCount() > 0){
28242             this.store.each(function(r){
28243                 if(r.data[prop] == value){
28244                     record = r;
28245                     return false;
28246                 }
28247                 return true;
28248             });
28249         }
28250         return record;
28251     },
28252     
28253     getName: function()
28254     {
28255         // returns hidden if it's set..
28256         if (!this.rendered) {return ''};
28257         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28258         
28259     },
28260      
28261
28262     
28263
28264     // private
28265     onEmptyResults : function(){
28266         Roo.log('empty results');
28267         //this.collapse();
28268     },
28269
28270     /**
28271      * Returns true if the dropdown list is expanded, else false.
28272      */
28273     isExpanded : function(){
28274         return false;
28275     },
28276
28277     /**
28278      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28279      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28280      * @param {String} value The data value of the item to select
28281      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28282      * selected item if it is not currently in view (defaults to true)
28283      * @return {Boolean} True if the value matched an item in the list, else false
28284      */
28285     selectByValue : function(v, scrollIntoView){
28286         Roo.log('select By Value');
28287         return false;
28288     
28289         if(v !== undefined && v !== null){
28290             var r = this.findRecord(this.valueField || this.displayField, v);
28291             if(r){
28292                 this.select(this.store.indexOf(r), scrollIntoView);
28293                 return true;
28294             }
28295         }
28296         return false;
28297     },
28298
28299     /**
28300      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28301      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28302      * @param {Number} index The zero-based index of the list item to select
28303      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28304      * selected item if it is not currently in view (defaults to true)
28305      */
28306     select : function(index, scrollIntoView){
28307         Roo.log('select ');
28308         return  ;
28309         
28310         this.selectedIndex = index;
28311         this.view.select(index);
28312         if(scrollIntoView !== false){
28313             var el = this.view.getNode(index);
28314             if(el){
28315                 this.innerList.scrollChildIntoView(el, false);
28316             }
28317         }
28318     },
28319
28320       
28321
28322     // private
28323     validateBlur : function(){
28324         
28325         return;
28326         
28327     },
28328
28329     // private
28330     initQuery : function(){
28331         this.doQuery(this.getRawValue());
28332     },
28333
28334     // private
28335     doForce : function(){
28336         if(this.el.dom.value.length > 0){
28337             this.el.dom.value =
28338                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28339              
28340         }
28341     },
28342
28343     /**
28344      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28345      * query allowing the query action to be canceled if needed.
28346      * @param {String} query The SQL query to execute
28347      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28348      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28349      * saved in the current store (defaults to false)
28350      */
28351     doQuery : function(q, forceAll){
28352         
28353         Roo.log('doQuery?');
28354         if(q === undefined || q === null){
28355             q = '';
28356         }
28357         var qe = {
28358             query: q,
28359             forceAll: forceAll,
28360             combo: this,
28361             cancel:false
28362         };
28363         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28364             return false;
28365         }
28366         q = qe.query;
28367         forceAll = qe.forceAll;
28368         if(forceAll === true || (q.length >= this.minChars)){
28369             if(this.lastQuery != q || this.alwaysQuery){
28370                 this.lastQuery = q;
28371                 if(this.mode == 'local'){
28372                     this.selectedIndex = -1;
28373                     if(forceAll){
28374                         this.store.clearFilter();
28375                     }else{
28376                         this.store.filter(this.displayField, q);
28377                     }
28378                     this.onLoad();
28379                 }else{
28380                     this.store.baseParams[this.queryParam] = q;
28381                     this.store.load({
28382                         params: this.getParams(q)
28383                     });
28384                     this.expand();
28385                 }
28386             }else{
28387                 this.selectedIndex = -1;
28388                 this.onLoad();   
28389             }
28390         }
28391     },
28392
28393     // private
28394     getParams : function(q){
28395         var p = {};
28396         //p[this.queryParam] = q;
28397         if(this.pageSize){
28398             p.start = 0;
28399             p.limit = this.pageSize;
28400         }
28401         return p;
28402     },
28403
28404     /**
28405      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28406      */
28407     collapse : function(){
28408         
28409     },
28410
28411     // private
28412     collapseIf : function(e){
28413         
28414     },
28415
28416     /**
28417      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28418      */
28419     expand : function(){
28420         
28421     } ,
28422
28423     // private
28424      
28425
28426     /** 
28427     * @cfg {Boolean} grow 
28428     * @hide 
28429     */
28430     /** 
28431     * @cfg {Number} growMin 
28432     * @hide 
28433     */
28434     /** 
28435     * @cfg {Number} growMax 
28436     * @hide 
28437     */
28438     /**
28439      * @hide
28440      * @method autoSize
28441      */
28442     
28443     setWidth : function()
28444     {
28445         
28446     },
28447     getResizeEl : function(){
28448         return this.el;
28449     }
28450 });//<script type="text/javasscript">
28451  
28452
28453 /**
28454  * @class Roo.DDView
28455  * A DnD enabled version of Roo.View.
28456  * @param {Element/String} container The Element in which to create the View.
28457  * @param {String} tpl The template string used to create the markup for each element of the View
28458  * @param {Object} config The configuration properties. These include all the config options of
28459  * {@link Roo.View} plus some specific to this class.<br>
28460  * <p>
28461  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28462  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28463  * <p>
28464  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28465 .x-view-drag-insert-above {
28466         border-top:1px dotted #3366cc;
28467 }
28468 .x-view-drag-insert-below {
28469         border-bottom:1px dotted #3366cc;
28470 }
28471 </code></pre>
28472  * 
28473  */
28474  
28475 Roo.DDView = function(container, tpl, config) {
28476     Roo.DDView.superclass.constructor.apply(this, arguments);
28477     this.getEl().setStyle("outline", "0px none");
28478     this.getEl().unselectable();
28479     if (this.dragGroup) {
28480         this.setDraggable(this.dragGroup.split(","));
28481     }
28482     if (this.dropGroup) {
28483         this.setDroppable(this.dropGroup.split(","));
28484     }
28485     if (this.deletable) {
28486         this.setDeletable();
28487     }
28488     this.isDirtyFlag = false;
28489         this.addEvents({
28490                 "drop" : true
28491         });
28492 };
28493
28494 Roo.extend(Roo.DDView, Roo.View, {
28495 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28496 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28497 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28498 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28499
28500         isFormField: true,
28501
28502         reset: Roo.emptyFn,
28503         
28504         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28505
28506         validate: function() {
28507                 return true;
28508         },
28509         
28510         destroy: function() {
28511                 this.purgeListeners();
28512                 this.getEl.removeAllListeners();
28513                 this.getEl().remove();
28514                 if (this.dragZone) {
28515                         if (this.dragZone.destroy) {
28516                                 this.dragZone.destroy();
28517                         }
28518                 }
28519                 if (this.dropZone) {
28520                         if (this.dropZone.destroy) {
28521                                 this.dropZone.destroy();
28522                         }
28523                 }
28524         },
28525
28526 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28527         getName: function() {
28528                 return this.name;
28529         },
28530
28531 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28532         setValue: function(v) {
28533                 if (!this.store) {
28534                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28535                 }
28536                 var data = {};
28537                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28538                 this.store.proxy = new Roo.data.MemoryProxy(data);
28539                 this.store.load();
28540         },
28541
28542 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28543         getValue: function() {
28544                 var result = '(';
28545                 this.store.each(function(rec) {
28546                         result += rec.id + ',';
28547                 });
28548                 return result.substr(0, result.length - 1) + ')';
28549         },
28550         
28551         getIds: function() {
28552                 var i = 0, result = new Array(this.store.getCount());
28553                 this.store.each(function(rec) {
28554                         result[i++] = rec.id;
28555                 });
28556                 return result;
28557         },
28558         
28559         isDirty: function() {
28560                 return this.isDirtyFlag;
28561         },
28562
28563 /**
28564  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28565  *      whole Element becomes the target, and this causes the drop gesture to append.
28566  */
28567     getTargetFromEvent : function(e) {
28568                 var target = e.getTarget();
28569                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28570                 target = target.parentNode;
28571                 }
28572                 if (!target) {
28573                         target = this.el.dom.lastChild || this.el.dom;
28574                 }
28575                 return target;
28576     },
28577
28578 /**
28579  *      Create the drag data which consists of an object which has the property "ddel" as
28580  *      the drag proxy element. 
28581  */
28582     getDragData : function(e) {
28583         var target = this.findItemFromChild(e.getTarget());
28584                 if(target) {
28585                         this.handleSelection(e);
28586                         var selNodes = this.getSelectedNodes();
28587             var dragData = {
28588                 source: this,
28589                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28590                 nodes: selNodes,
28591                 records: []
28592                         };
28593                         var selectedIndices = this.getSelectedIndexes();
28594                         for (var i = 0; i < selectedIndices.length; i++) {
28595                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28596                         }
28597                         if (selNodes.length == 1) {
28598                                 dragData.ddel = target.cloneNode(true); // the div element
28599                         } else {
28600                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28601                                 div.className = 'multi-proxy';
28602                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28603                                         div.appendChild(selNodes[i].cloneNode(true));
28604                                 }
28605                                 dragData.ddel = div;
28606                         }
28607             //console.log(dragData)
28608             //console.log(dragData.ddel.innerHTML)
28609                         return dragData;
28610                 }
28611         //console.log('nodragData')
28612                 return false;
28613     },
28614     
28615 /**     Specify to which ddGroup items in this DDView may be dragged. */
28616     setDraggable: function(ddGroup) {
28617         if (ddGroup instanceof Array) {
28618                 Roo.each(ddGroup, this.setDraggable, this);
28619                 return;
28620         }
28621         if (this.dragZone) {
28622                 this.dragZone.addToGroup(ddGroup);
28623         } else {
28624                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28625                                 containerScroll: true,
28626                                 ddGroup: ddGroup 
28627
28628                         });
28629 //                      Draggability implies selection. DragZone's mousedown selects the element.
28630                         if (!this.multiSelect) { this.singleSelect = true; }
28631
28632 //                      Wire the DragZone's handlers up to methods in *this*
28633                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28634                 }
28635     },
28636
28637 /**     Specify from which ddGroup this DDView accepts drops. */
28638     setDroppable: function(ddGroup) {
28639         if (ddGroup instanceof Array) {
28640                 Roo.each(ddGroup, this.setDroppable, this);
28641                 return;
28642         }
28643         if (this.dropZone) {
28644                 this.dropZone.addToGroup(ddGroup);
28645         } else {
28646                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28647                                 containerScroll: true,
28648                                 ddGroup: ddGroup
28649                         });
28650
28651 //                      Wire the DropZone's handlers up to methods in *this*
28652                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28653                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28654                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28655                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28656                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28657                 }
28658     },
28659
28660 /**     Decide whether to drop above or below a View node. */
28661     getDropPoint : function(e, n, dd){
28662         if (n == this.el.dom) { return "above"; }
28663                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28664                 var c = t + (b - t) / 2;
28665                 var y = Roo.lib.Event.getPageY(e);
28666                 if(y <= c) {
28667                         return "above";
28668                 }else{
28669                         return "below";
28670                 }
28671     },
28672
28673     onNodeEnter : function(n, dd, e, data){
28674                 return false;
28675     },
28676     
28677     onNodeOver : function(n, dd, e, data){
28678                 var pt = this.getDropPoint(e, n, dd);
28679                 // set the insert point style on the target node
28680                 var dragElClass = this.dropNotAllowed;
28681                 if (pt) {
28682                         var targetElClass;
28683                         if (pt == "above"){
28684                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28685                                 targetElClass = "x-view-drag-insert-above";
28686                         } else {
28687                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28688                                 targetElClass = "x-view-drag-insert-below";
28689                         }
28690                         if (this.lastInsertClass != targetElClass){
28691                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28692                                 this.lastInsertClass = targetElClass;
28693                         }
28694                 }
28695                 return dragElClass;
28696         },
28697
28698     onNodeOut : function(n, dd, e, data){
28699                 this.removeDropIndicators(n);
28700     },
28701
28702     onNodeDrop : function(n, dd, e, data){
28703         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28704                 return false;
28705         }
28706         var pt = this.getDropPoint(e, n, dd);
28707                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28708                 if (pt == "below") { insertAt++; }
28709                 for (var i = 0; i < data.records.length; i++) {
28710                         var r = data.records[i];
28711                         var dup = this.store.getById(r.id);
28712                         if (dup && (dd != this.dragZone)) {
28713                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28714                         } else {
28715                                 if (data.copy) {
28716                                         this.store.insert(insertAt++, r.copy());
28717                                 } else {
28718                                         data.source.isDirtyFlag = true;
28719                                         r.store.remove(r);
28720                                         this.store.insert(insertAt++, r);
28721                                 }
28722                                 this.isDirtyFlag = true;
28723                         }
28724                 }
28725                 this.dragZone.cachedTarget = null;
28726                 return true;
28727     },
28728
28729     removeDropIndicators : function(n){
28730                 if(n){
28731                         Roo.fly(n).removeClass([
28732                                 "x-view-drag-insert-above",
28733                                 "x-view-drag-insert-below"]);
28734                         this.lastInsertClass = "_noclass";
28735                 }
28736     },
28737
28738 /**
28739  *      Utility method. Add a delete option to the DDView's context menu.
28740  *      @param {String} imageUrl The URL of the "delete" icon image.
28741  */
28742         setDeletable: function(imageUrl) {
28743                 if (!this.singleSelect && !this.multiSelect) {
28744                         this.singleSelect = true;
28745                 }
28746                 var c = this.getContextMenu();
28747                 this.contextMenu.on("itemclick", function(item) {
28748                         switch (item.id) {
28749                                 case "delete":
28750                                         this.remove(this.getSelectedIndexes());
28751                                         break;
28752                         }
28753                 }, this);
28754                 this.contextMenu.add({
28755                         icon: imageUrl,
28756                         id: "delete",
28757                         text: 'Delete'
28758                 });
28759         },
28760         
28761 /**     Return the context menu for this DDView. */
28762         getContextMenu: function() {
28763                 if (!this.contextMenu) {
28764 //                      Create the View's context menu
28765                         this.contextMenu = new Roo.menu.Menu({
28766                                 id: this.id + "-contextmenu"
28767                         });
28768                         this.el.on("contextmenu", this.showContextMenu, this);
28769                 }
28770                 return this.contextMenu;
28771         },
28772         
28773         disableContextMenu: function() {
28774                 if (this.contextMenu) {
28775                         this.el.un("contextmenu", this.showContextMenu, this);
28776                 }
28777         },
28778
28779         showContextMenu: function(e, item) {
28780         item = this.findItemFromChild(e.getTarget());
28781                 if (item) {
28782                         e.stopEvent();
28783                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28784                         this.contextMenu.showAt(e.getXY());
28785             }
28786     },
28787
28788 /**
28789  *      Remove {@link Roo.data.Record}s at the specified indices.
28790  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28791  */
28792     remove: function(selectedIndices) {
28793                 selectedIndices = [].concat(selectedIndices);
28794                 for (var i = 0; i < selectedIndices.length; i++) {
28795                         var rec = this.store.getAt(selectedIndices[i]);
28796                         this.store.remove(rec);
28797                 }
28798     },
28799
28800 /**
28801  *      Double click fires the event, but also, if this is draggable, and there is only one other
28802  *      related DropZone, it transfers the selected node.
28803  */
28804     onDblClick : function(e){
28805         var item = this.findItemFromChild(e.getTarget());
28806         if(item){
28807             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28808                 return false;
28809             }
28810             if (this.dragGroup) {
28811                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28812                     while (targets.indexOf(this.dropZone) > -1) {
28813                             targets.remove(this.dropZone);
28814                                 }
28815                     if (targets.length == 1) {
28816                                         this.dragZone.cachedTarget = null;
28817                         var el = Roo.get(targets[0].getEl());
28818                         var box = el.getBox(true);
28819                         targets[0].onNodeDrop(el.dom, {
28820                                 target: el.dom,
28821                                 xy: [box.x, box.y + box.height - 1]
28822                         }, null, this.getDragData(e));
28823                     }
28824                 }
28825         }
28826     },
28827     
28828     handleSelection: function(e) {
28829                 this.dragZone.cachedTarget = null;
28830         var item = this.findItemFromChild(e.getTarget());
28831         if (!item) {
28832                 this.clearSelections(true);
28833                 return;
28834         }
28835                 if (item && (this.multiSelect || this.singleSelect)){
28836                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28837                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28838                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28839                                 this.unselect(item);
28840                         } else {
28841                                 this.select(item, this.multiSelect && e.ctrlKey);
28842                                 this.lastSelection = item;
28843                         }
28844                 }
28845     },
28846
28847     onItemClick : function(item, index, e){
28848                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28849                         return false;
28850                 }
28851                 return true;
28852     },
28853
28854     unselect : function(nodeInfo, suppressEvent){
28855                 var node = this.getNode(nodeInfo);
28856                 if(node && this.isSelected(node)){
28857                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28858                                 Roo.fly(node).removeClass(this.selectedClass);
28859                                 this.selections.remove(node);
28860                                 if(!suppressEvent){
28861                                         this.fireEvent("selectionchange", this, this.selections);
28862                                 }
28863                         }
28864                 }
28865     }
28866 });
28867 /*
28868  * Based on:
28869  * Ext JS Library 1.1.1
28870  * Copyright(c) 2006-2007, Ext JS, LLC.
28871  *
28872  * Originally Released Under LGPL - original licence link has changed is not relivant.
28873  *
28874  * Fork - LGPL
28875  * <script type="text/javascript">
28876  */
28877  
28878 /**
28879  * @class Roo.LayoutManager
28880  * @extends Roo.util.Observable
28881  * Base class for layout managers.
28882  */
28883 Roo.LayoutManager = function(container, config){
28884     Roo.LayoutManager.superclass.constructor.call(this);
28885     this.el = Roo.get(container);
28886     // ie scrollbar fix
28887     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28888         document.body.scroll = "no";
28889     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28890         this.el.position('relative');
28891     }
28892     this.id = this.el.id;
28893     this.el.addClass("x-layout-container");
28894     /** false to disable window resize monitoring @type Boolean */
28895     this.monitorWindowResize = true;
28896     this.regions = {};
28897     this.addEvents({
28898         /**
28899          * @event layout
28900          * Fires when a layout is performed. 
28901          * @param {Roo.LayoutManager} this
28902          */
28903         "layout" : true,
28904         /**
28905          * @event regionresized
28906          * Fires when the user resizes a region. 
28907          * @param {Roo.LayoutRegion} region The resized region
28908          * @param {Number} newSize The new size (width for east/west, height for north/south)
28909          */
28910         "regionresized" : true,
28911         /**
28912          * @event regioncollapsed
28913          * Fires when a region is collapsed. 
28914          * @param {Roo.LayoutRegion} region The collapsed region
28915          */
28916         "regioncollapsed" : true,
28917         /**
28918          * @event regionexpanded
28919          * Fires when a region is expanded.  
28920          * @param {Roo.LayoutRegion} region The expanded region
28921          */
28922         "regionexpanded" : true
28923     });
28924     this.updating = false;
28925     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28926 };
28927
28928 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28929     /**
28930      * Returns true if this layout is currently being updated
28931      * @return {Boolean}
28932      */
28933     isUpdating : function(){
28934         return this.updating; 
28935     },
28936     
28937     /**
28938      * Suspend the LayoutManager from doing auto-layouts while
28939      * making multiple add or remove calls
28940      */
28941     beginUpdate : function(){
28942         this.updating = true;    
28943     },
28944     
28945     /**
28946      * Restore auto-layouts and optionally disable the manager from performing a layout
28947      * @param {Boolean} noLayout true to disable a layout update 
28948      */
28949     endUpdate : function(noLayout){
28950         this.updating = false;
28951         if(!noLayout){
28952             this.layout();
28953         }    
28954     },
28955     
28956     layout: function(){
28957         
28958     },
28959     
28960     onRegionResized : function(region, newSize){
28961         this.fireEvent("regionresized", region, newSize);
28962         this.layout();
28963     },
28964     
28965     onRegionCollapsed : function(region){
28966         this.fireEvent("regioncollapsed", region);
28967     },
28968     
28969     onRegionExpanded : function(region){
28970         this.fireEvent("regionexpanded", region);
28971     },
28972         
28973     /**
28974      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28975      * performs box-model adjustments.
28976      * @return {Object} The size as an object {width: (the width), height: (the height)}
28977      */
28978     getViewSize : function(){
28979         var size;
28980         if(this.el.dom != document.body){
28981             size = this.el.getSize();
28982         }else{
28983             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28984         }
28985         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28986         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28987         return size;
28988     },
28989     
28990     /**
28991      * Returns the Element this layout is bound to.
28992      * @return {Roo.Element}
28993      */
28994     getEl : function(){
28995         return this.el;
28996     },
28997     
28998     /**
28999      * Returns the specified region.
29000      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29001      * @return {Roo.LayoutRegion}
29002      */
29003     getRegion : function(target){
29004         return this.regions[target.toLowerCase()];
29005     },
29006     
29007     onWindowResize : function(){
29008         if(this.monitorWindowResize){
29009             this.layout();
29010         }
29011     }
29012 });/*
29013  * Based on:
29014  * Ext JS Library 1.1.1
29015  * Copyright(c) 2006-2007, Ext JS, LLC.
29016  *
29017  * Originally Released Under LGPL - original licence link has changed is not relivant.
29018  *
29019  * Fork - LGPL
29020  * <script type="text/javascript">
29021  */
29022 /**
29023  * @class Roo.BorderLayout
29024  * @extends Roo.LayoutManager
29025  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29026  * please see: <br><br>
29027  * <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>
29028  * <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>
29029  * Example:
29030  <pre><code>
29031  var layout = new Roo.BorderLayout(document.body, {
29032     north: {
29033         initialSize: 25,
29034         titlebar: false
29035     },
29036     west: {
29037         split:true,
29038         initialSize: 200,
29039         minSize: 175,
29040         maxSize: 400,
29041         titlebar: true,
29042         collapsible: true
29043     },
29044     east: {
29045         split:true,
29046         initialSize: 202,
29047         minSize: 175,
29048         maxSize: 400,
29049         titlebar: true,
29050         collapsible: true
29051     },
29052     south: {
29053         split:true,
29054         initialSize: 100,
29055         minSize: 100,
29056         maxSize: 200,
29057         titlebar: true,
29058         collapsible: true
29059     },
29060     center: {
29061         titlebar: true,
29062         autoScroll:true,
29063         resizeTabs: true,
29064         minTabWidth: 50,
29065         preferredTabWidth: 150
29066     }
29067 });
29068
29069 // shorthand
29070 var CP = Roo.ContentPanel;
29071
29072 layout.beginUpdate();
29073 layout.add("north", new CP("north", "North"));
29074 layout.add("south", new CP("south", {title: "South", closable: true}));
29075 layout.add("west", new CP("west", {title: "West"}));
29076 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29077 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29078 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29079 layout.getRegion("center").showPanel("center1");
29080 layout.endUpdate();
29081 </code></pre>
29082
29083 <b>The container the layout is rendered into can be either the body element or any other element.
29084 If it is not the body element, the container needs to either be an absolute positioned element,
29085 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29086 the container size if it is not the body element.</b>
29087
29088 * @constructor
29089 * Create a new BorderLayout
29090 * @param {String/HTMLElement/Element} container The container this layout is bound to
29091 * @param {Object} config Configuration options
29092  */
29093 Roo.BorderLayout = function(container, config){
29094     config = config || {};
29095     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29096     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29097     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29098         var target = this.factory.validRegions[i];
29099         if(config[target]){
29100             this.addRegion(target, config[target]);
29101         }
29102     }
29103 };
29104
29105 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29106     /**
29107      * Creates and adds a new region if it doesn't already exist.
29108      * @param {String} target The target region key (north, south, east, west or center).
29109      * @param {Object} config The regions config object
29110      * @return {BorderLayoutRegion} The new region
29111      */
29112     addRegion : function(target, config){
29113         if(!this.regions[target]){
29114             var r = this.factory.create(target, this, config);
29115             this.bindRegion(target, r);
29116         }
29117         return this.regions[target];
29118     },
29119
29120     // private (kinda)
29121     bindRegion : function(name, r){
29122         this.regions[name] = r;
29123         r.on("visibilitychange", this.layout, this);
29124         r.on("paneladded", this.layout, this);
29125         r.on("panelremoved", this.layout, this);
29126         r.on("invalidated", this.layout, this);
29127         r.on("resized", this.onRegionResized, this);
29128         r.on("collapsed", this.onRegionCollapsed, this);
29129         r.on("expanded", this.onRegionExpanded, this);
29130     },
29131
29132     /**
29133      * Performs a layout update.
29134      */
29135     layout : function(){
29136         if(this.updating) {
29137             return;
29138         }
29139         var size = this.getViewSize();
29140         var w = size.width;
29141         var h = size.height;
29142         var centerW = w;
29143         var centerH = h;
29144         var centerY = 0;
29145         var centerX = 0;
29146         //var x = 0, y = 0;
29147
29148         var rs = this.regions;
29149         var north = rs["north"];
29150         var south = rs["south"]; 
29151         var west = rs["west"];
29152         var east = rs["east"];
29153         var center = rs["center"];
29154         //if(this.hideOnLayout){ // not supported anymore
29155             //c.el.setStyle("display", "none");
29156         //}
29157         if(north && north.isVisible()){
29158             var b = north.getBox();
29159             var m = north.getMargins();
29160             b.width = w - (m.left+m.right);
29161             b.x = m.left;
29162             b.y = m.top;
29163             centerY = b.height + b.y + m.bottom;
29164             centerH -= centerY;
29165             north.updateBox(this.safeBox(b));
29166         }
29167         if(south && south.isVisible()){
29168             var b = south.getBox();
29169             var m = south.getMargins();
29170             b.width = w - (m.left+m.right);
29171             b.x = m.left;
29172             var totalHeight = (b.height + m.top + m.bottom);
29173             b.y = h - totalHeight + m.top;
29174             centerH -= totalHeight;
29175             south.updateBox(this.safeBox(b));
29176         }
29177         if(west && west.isVisible()){
29178             var b = west.getBox();
29179             var m = west.getMargins();
29180             b.height = centerH - (m.top+m.bottom);
29181             b.x = m.left;
29182             b.y = centerY + m.top;
29183             var totalWidth = (b.width + m.left + m.right);
29184             centerX += totalWidth;
29185             centerW -= totalWidth;
29186             west.updateBox(this.safeBox(b));
29187         }
29188         if(east && east.isVisible()){
29189             var b = east.getBox();
29190             var m = east.getMargins();
29191             b.height = centerH - (m.top+m.bottom);
29192             var totalWidth = (b.width + m.left + m.right);
29193             b.x = w - totalWidth + m.left;
29194             b.y = centerY + m.top;
29195             centerW -= totalWidth;
29196             east.updateBox(this.safeBox(b));
29197         }
29198         if(center){
29199             var m = center.getMargins();
29200             var centerBox = {
29201                 x: centerX + m.left,
29202                 y: centerY + m.top,
29203                 width: centerW - (m.left+m.right),
29204                 height: centerH - (m.top+m.bottom)
29205             };
29206             //if(this.hideOnLayout){
29207                 //center.el.setStyle("display", "block");
29208             //}
29209             center.updateBox(this.safeBox(centerBox));
29210         }
29211         this.el.repaint();
29212         this.fireEvent("layout", this);
29213     },
29214
29215     // private
29216     safeBox : function(box){
29217         box.width = Math.max(0, box.width);
29218         box.height = Math.max(0, box.height);
29219         return box;
29220     },
29221
29222     /**
29223      * Adds a ContentPanel (or subclass) to this layout.
29224      * @param {String} target The target region key (north, south, east, west or center).
29225      * @param {Roo.ContentPanel} panel The panel to add
29226      * @return {Roo.ContentPanel} The added panel
29227      */
29228     add : function(target, panel){
29229          
29230         target = target.toLowerCase();
29231         return this.regions[target].add(panel);
29232     },
29233
29234     /**
29235      * Remove a ContentPanel (or subclass) to this layout.
29236      * @param {String} target The target region key (north, south, east, west or center).
29237      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29238      * @return {Roo.ContentPanel} The removed panel
29239      */
29240     remove : function(target, panel){
29241         target = target.toLowerCase();
29242         return this.regions[target].remove(panel);
29243     },
29244
29245     /**
29246      * Searches all regions for a panel with the specified id
29247      * @param {String} panelId
29248      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29249      */
29250     findPanel : function(panelId){
29251         var rs = this.regions;
29252         for(var target in rs){
29253             if(typeof rs[target] != "function"){
29254                 var p = rs[target].getPanel(panelId);
29255                 if(p){
29256                     return p;
29257                 }
29258             }
29259         }
29260         return null;
29261     },
29262
29263     /**
29264      * Searches all regions for a panel with the specified id and activates (shows) it.
29265      * @param {String/ContentPanel} panelId The panels id or the panel itself
29266      * @return {Roo.ContentPanel} The shown panel or null
29267      */
29268     showPanel : function(panelId) {
29269       var rs = this.regions;
29270       for(var target in rs){
29271          var r = rs[target];
29272          if(typeof r != "function"){
29273             if(r.hasPanel(panelId)){
29274                return r.showPanel(panelId);
29275             }
29276          }
29277       }
29278       return null;
29279    },
29280
29281    /**
29282      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29283      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29284      */
29285     restoreState : function(provider){
29286         if(!provider){
29287             provider = Roo.state.Manager;
29288         }
29289         var sm = new Roo.LayoutStateManager();
29290         sm.init(this, provider);
29291     },
29292
29293     /**
29294      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29295      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29296      * a valid ContentPanel config object.  Example:
29297      * <pre><code>
29298 // Create the main layout
29299 var layout = new Roo.BorderLayout('main-ct', {
29300     west: {
29301         split:true,
29302         minSize: 175,
29303         titlebar: true
29304     },
29305     center: {
29306         title:'Components'
29307     }
29308 }, 'main-ct');
29309
29310 // Create and add multiple ContentPanels at once via configs
29311 layout.batchAdd({
29312    west: {
29313        id: 'source-files',
29314        autoCreate:true,
29315        title:'Ext Source Files',
29316        autoScroll:true,
29317        fitToFrame:true
29318    },
29319    center : {
29320        el: cview,
29321        autoScroll:true,
29322        fitToFrame:true,
29323        toolbar: tb,
29324        resizeEl:'cbody'
29325    }
29326 });
29327 </code></pre>
29328      * @param {Object} regions An object containing ContentPanel configs by region name
29329      */
29330     batchAdd : function(regions){
29331         this.beginUpdate();
29332         for(var rname in regions){
29333             var lr = this.regions[rname];
29334             if(lr){
29335                 this.addTypedPanels(lr, regions[rname]);
29336             }
29337         }
29338         this.endUpdate();
29339     },
29340
29341     // private
29342     addTypedPanels : function(lr, ps){
29343         if(typeof ps == 'string'){
29344             lr.add(new Roo.ContentPanel(ps));
29345         }
29346         else if(ps instanceof Array){
29347             for(var i =0, len = ps.length; i < len; i++){
29348                 this.addTypedPanels(lr, ps[i]);
29349             }
29350         }
29351         else if(!ps.events){ // raw config?
29352             var el = ps.el;
29353             delete ps.el; // prevent conflict
29354             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29355         }
29356         else {  // panel object assumed!
29357             lr.add(ps);
29358         }
29359     },
29360     /**
29361      * Adds a xtype elements to the layout.
29362      * <pre><code>
29363
29364 layout.addxtype({
29365        xtype : 'ContentPanel',
29366        region: 'west',
29367        items: [ .... ]
29368    }
29369 );
29370
29371 layout.addxtype({
29372         xtype : 'NestedLayoutPanel',
29373         region: 'west',
29374         layout: {
29375            center: { },
29376            west: { }   
29377         },
29378         items : [ ... list of content panels or nested layout panels.. ]
29379    }
29380 );
29381 </code></pre>
29382      * @param {Object} cfg Xtype definition of item to add.
29383      */
29384     addxtype : function(cfg)
29385     {
29386         // basically accepts a pannel...
29387         // can accept a layout region..!?!?
29388         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29389         
29390         if (!cfg.xtype.match(/Panel$/)) {
29391             return false;
29392         }
29393         var ret = false;
29394         
29395         if (typeof(cfg.region) == 'undefined') {
29396             Roo.log("Failed to add Panel, region was not set");
29397             Roo.log(cfg);
29398             return false;
29399         }
29400         var region = cfg.region;
29401         delete cfg.region;
29402         
29403           
29404         var xitems = [];
29405         if (cfg.items) {
29406             xitems = cfg.items;
29407             delete cfg.items;
29408         }
29409         var nb = false;
29410         
29411         switch(cfg.xtype) 
29412         {
29413             case 'ContentPanel':  // ContentPanel (el, cfg)
29414             case 'ScrollPanel':  // ContentPanel (el, cfg)
29415             case 'ViewPanel': 
29416                 if(cfg.autoCreate) {
29417                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29418                 } else {
29419                     var el = this.el.createChild();
29420                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29421                 }
29422                 
29423                 this.add(region, ret);
29424                 break;
29425             
29426             
29427             case 'TreePanel': // our new panel!
29428                 cfg.el = this.el.createChild();
29429                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29430                 this.add(region, ret);
29431                 break;
29432             
29433             case 'NestedLayoutPanel': 
29434                 // create a new Layout (which is  a Border Layout...
29435                 var el = this.el.createChild();
29436                 var clayout = cfg.layout;
29437                 delete cfg.layout;
29438                 clayout.items   = clayout.items  || [];
29439                 // replace this exitems with the clayout ones..
29440                 xitems = clayout.items;
29441                  
29442                 
29443                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29444                     cfg.background = false;
29445                 }
29446                 var layout = new Roo.BorderLayout(el, clayout);
29447                 
29448                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29449                 //console.log('adding nested layout panel '  + cfg.toSource());
29450                 this.add(region, ret);
29451                 nb = {}; /// find first...
29452                 break;
29453                 
29454             case 'GridPanel': 
29455             
29456                 // needs grid and region
29457                 
29458                 //var el = this.getRegion(region).el.createChild();
29459                 var el = this.el.createChild();
29460                 // create the grid first...
29461                 
29462                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29463                 delete cfg.grid;
29464                 if (region == 'center' && this.active ) {
29465                     cfg.background = false;
29466                 }
29467                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29468                 
29469                 this.add(region, ret);
29470                 if (cfg.background) {
29471                     ret.on('activate', function(gp) {
29472                         if (!gp.grid.rendered) {
29473                             gp.grid.render();
29474                         }
29475                     });
29476                 } else {
29477                     grid.render();
29478                 }
29479                 break;
29480            
29481            
29482            
29483                 
29484                 
29485                 
29486             default:
29487                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29488                     
29489                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29490                     this.add(region, ret);
29491                 } else {
29492                 
29493                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29494                     return null;
29495                 }
29496                 
29497              // GridPanel (grid, cfg)
29498             
29499         }
29500         this.beginUpdate();
29501         // add children..
29502         var region = '';
29503         var abn = {};
29504         Roo.each(xitems, function(i)  {
29505             region = nb && i.region ? i.region : false;
29506             
29507             var add = ret.addxtype(i);
29508            
29509             if (region) {
29510                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29511                 if (!i.background) {
29512                     abn[region] = nb[region] ;
29513                 }
29514             }
29515             
29516         });
29517         this.endUpdate();
29518
29519         // make the last non-background panel active..
29520         //if (nb) { Roo.log(abn); }
29521         if (nb) {
29522             
29523             for(var r in abn) {
29524                 region = this.getRegion(r);
29525                 if (region) {
29526                     // tried using nb[r], but it does not work..
29527                      
29528                     region.showPanel(abn[r]);
29529                    
29530                 }
29531             }
29532         }
29533         return ret;
29534         
29535     }
29536 });
29537
29538 /**
29539  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29540  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29541  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29542  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29543  * <pre><code>
29544 // shorthand
29545 var CP = Roo.ContentPanel;
29546
29547 var layout = Roo.BorderLayout.create({
29548     north: {
29549         initialSize: 25,
29550         titlebar: false,
29551         panels: [new CP("north", "North")]
29552     },
29553     west: {
29554         split:true,
29555         initialSize: 200,
29556         minSize: 175,
29557         maxSize: 400,
29558         titlebar: true,
29559         collapsible: true,
29560         panels: [new CP("west", {title: "West"})]
29561     },
29562     east: {
29563         split:true,
29564         initialSize: 202,
29565         minSize: 175,
29566         maxSize: 400,
29567         titlebar: true,
29568         collapsible: true,
29569         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29570     },
29571     south: {
29572         split:true,
29573         initialSize: 100,
29574         minSize: 100,
29575         maxSize: 200,
29576         titlebar: true,
29577         collapsible: true,
29578         panels: [new CP("south", {title: "South", closable: true})]
29579     },
29580     center: {
29581         titlebar: true,
29582         autoScroll:true,
29583         resizeTabs: true,
29584         minTabWidth: 50,
29585         preferredTabWidth: 150,
29586         panels: [
29587             new CP("center1", {title: "Close Me", closable: true}),
29588             new CP("center2", {title: "Center Panel", closable: false})
29589         ]
29590     }
29591 }, document.body);
29592
29593 layout.getRegion("center").showPanel("center1");
29594 </code></pre>
29595  * @param config
29596  * @param targetEl
29597  */
29598 Roo.BorderLayout.create = function(config, targetEl){
29599     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29600     layout.beginUpdate();
29601     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29602     for(var j = 0, jlen = regions.length; j < jlen; j++){
29603         var lr = regions[j];
29604         if(layout.regions[lr] && config[lr].panels){
29605             var r = layout.regions[lr];
29606             var ps = config[lr].panels;
29607             layout.addTypedPanels(r, ps);
29608         }
29609     }
29610     layout.endUpdate();
29611     return layout;
29612 };
29613
29614 // private
29615 Roo.BorderLayout.RegionFactory = {
29616     // private
29617     validRegions : ["north","south","east","west","center"],
29618
29619     // private
29620     create : function(target, mgr, config){
29621         target = target.toLowerCase();
29622         if(config.lightweight || config.basic){
29623             return new Roo.BasicLayoutRegion(mgr, config, target);
29624         }
29625         switch(target){
29626             case "north":
29627                 return new Roo.NorthLayoutRegion(mgr, config);
29628             case "south":
29629                 return new Roo.SouthLayoutRegion(mgr, config);
29630             case "east":
29631                 return new Roo.EastLayoutRegion(mgr, config);
29632             case "west":
29633                 return new Roo.WestLayoutRegion(mgr, config);
29634             case "center":
29635                 return new Roo.CenterLayoutRegion(mgr, config);
29636         }
29637         throw 'Layout region "'+target+'" not supported.';
29638     }
29639 };/*
29640  * Based on:
29641  * Ext JS Library 1.1.1
29642  * Copyright(c) 2006-2007, Ext JS, LLC.
29643  *
29644  * Originally Released Under LGPL - original licence link has changed is not relivant.
29645  *
29646  * Fork - LGPL
29647  * <script type="text/javascript">
29648  */
29649  
29650 /**
29651  * @class Roo.BasicLayoutRegion
29652  * @extends Roo.util.Observable
29653  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29654  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29655  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29656  */
29657 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29658     this.mgr = mgr;
29659     this.position  = pos;
29660     this.events = {
29661         /**
29662          * @scope Roo.BasicLayoutRegion
29663          */
29664         
29665         /**
29666          * @event beforeremove
29667          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29668          * @param {Roo.LayoutRegion} this
29669          * @param {Roo.ContentPanel} panel The panel
29670          * @param {Object} e The cancel event object
29671          */
29672         "beforeremove" : true,
29673         /**
29674          * @event invalidated
29675          * Fires when the layout for this region is changed.
29676          * @param {Roo.LayoutRegion} this
29677          */
29678         "invalidated" : true,
29679         /**
29680          * @event visibilitychange
29681          * Fires when this region is shown or hidden 
29682          * @param {Roo.LayoutRegion} this
29683          * @param {Boolean} visibility true or false
29684          */
29685         "visibilitychange" : true,
29686         /**
29687          * @event paneladded
29688          * Fires when a panel is added. 
29689          * @param {Roo.LayoutRegion} this
29690          * @param {Roo.ContentPanel} panel The panel
29691          */
29692         "paneladded" : true,
29693         /**
29694          * @event panelremoved
29695          * Fires when a panel is removed. 
29696          * @param {Roo.LayoutRegion} this
29697          * @param {Roo.ContentPanel} panel The panel
29698          */
29699         "panelremoved" : true,
29700         /**
29701          * @event beforecollapse
29702          * Fires when this region before collapse.
29703          * @param {Roo.LayoutRegion} this
29704          */
29705         "beforecollapse" : true,
29706         /**
29707          * @event collapsed
29708          * Fires when this region is collapsed.
29709          * @param {Roo.LayoutRegion} this
29710          */
29711         "collapsed" : true,
29712         /**
29713          * @event expanded
29714          * Fires when this region is expanded.
29715          * @param {Roo.LayoutRegion} this
29716          */
29717         "expanded" : true,
29718         /**
29719          * @event slideshow
29720          * Fires when this region is slid into view.
29721          * @param {Roo.LayoutRegion} this
29722          */
29723         "slideshow" : true,
29724         /**
29725          * @event slidehide
29726          * Fires when this region slides out of view. 
29727          * @param {Roo.LayoutRegion} this
29728          */
29729         "slidehide" : true,
29730         /**
29731          * @event panelactivated
29732          * Fires when a panel is activated. 
29733          * @param {Roo.LayoutRegion} this
29734          * @param {Roo.ContentPanel} panel The activated panel
29735          */
29736         "panelactivated" : true,
29737         /**
29738          * @event resized
29739          * Fires when the user resizes this region. 
29740          * @param {Roo.LayoutRegion} this
29741          * @param {Number} newSize The new size (width for east/west, height for north/south)
29742          */
29743         "resized" : true
29744     };
29745     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29746     this.panels = new Roo.util.MixedCollection();
29747     this.panels.getKey = this.getPanelId.createDelegate(this);
29748     this.box = null;
29749     this.activePanel = null;
29750     // ensure listeners are added...
29751     
29752     if (config.listeners || config.events) {
29753         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29754             listeners : config.listeners || {},
29755             events : config.events || {}
29756         });
29757     }
29758     
29759     if(skipConfig !== true){
29760         this.applyConfig(config);
29761     }
29762 };
29763
29764 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29765     getPanelId : function(p){
29766         return p.getId();
29767     },
29768     
29769     applyConfig : function(config){
29770         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29771         this.config = config;
29772         
29773     },
29774     
29775     /**
29776      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29777      * the width, for horizontal (north, south) the height.
29778      * @param {Number} newSize The new width or height
29779      */
29780     resizeTo : function(newSize){
29781         var el = this.el ? this.el :
29782                  (this.activePanel ? this.activePanel.getEl() : null);
29783         if(el){
29784             switch(this.position){
29785                 case "east":
29786                 case "west":
29787                     el.setWidth(newSize);
29788                     this.fireEvent("resized", this, newSize);
29789                 break;
29790                 case "north":
29791                 case "south":
29792                     el.setHeight(newSize);
29793                     this.fireEvent("resized", this, newSize);
29794                 break;                
29795             }
29796         }
29797     },
29798     
29799     getBox : function(){
29800         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29801     },
29802     
29803     getMargins : function(){
29804         return this.margins;
29805     },
29806     
29807     updateBox : function(box){
29808         this.box = box;
29809         var el = this.activePanel.getEl();
29810         el.dom.style.left = box.x + "px";
29811         el.dom.style.top = box.y + "px";
29812         this.activePanel.setSize(box.width, box.height);
29813     },
29814     
29815     /**
29816      * Returns the container element for this region.
29817      * @return {Roo.Element}
29818      */
29819     getEl : function(){
29820         return this.activePanel;
29821     },
29822     
29823     /**
29824      * Returns true if this region is currently visible.
29825      * @return {Boolean}
29826      */
29827     isVisible : function(){
29828         return this.activePanel ? true : false;
29829     },
29830     
29831     setActivePanel : function(panel){
29832         panel = this.getPanel(panel);
29833         if(this.activePanel && this.activePanel != panel){
29834             this.activePanel.setActiveState(false);
29835             this.activePanel.getEl().setLeftTop(-10000,-10000);
29836         }
29837         this.activePanel = panel;
29838         panel.setActiveState(true);
29839         if(this.box){
29840             panel.setSize(this.box.width, this.box.height);
29841         }
29842         this.fireEvent("panelactivated", this, panel);
29843         this.fireEvent("invalidated");
29844     },
29845     
29846     /**
29847      * Show the specified panel.
29848      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29849      * @return {Roo.ContentPanel} The shown panel or null
29850      */
29851     showPanel : function(panel){
29852         if(panel = this.getPanel(panel)){
29853             this.setActivePanel(panel);
29854         }
29855         return panel;
29856     },
29857     
29858     /**
29859      * Get the active panel for this region.
29860      * @return {Roo.ContentPanel} The active panel or null
29861      */
29862     getActivePanel : function(){
29863         return this.activePanel;
29864     },
29865     
29866     /**
29867      * Add the passed ContentPanel(s)
29868      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29869      * @return {Roo.ContentPanel} The panel added (if only one was added)
29870      */
29871     add : function(panel){
29872         if(arguments.length > 1){
29873             for(var i = 0, len = arguments.length; i < len; i++) {
29874                 this.add(arguments[i]);
29875             }
29876             return null;
29877         }
29878         if(this.hasPanel(panel)){
29879             this.showPanel(panel);
29880             return panel;
29881         }
29882         var el = panel.getEl();
29883         if(el.dom.parentNode != this.mgr.el.dom){
29884             this.mgr.el.dom.appendChild(el.dom);
29885         }
29886         if(panel.setRegion){
29887             panel.setRegion(this);
29888         }
29889         this.panels.add(panel);
29890         el.setStyle("position", "absolute");
29891         if(!panel.background){
29892             this.setActivePanel(panel);
29893             if(this.config.initialSize && this.panels.getCount()==1){
29894                 this.resizeTo(this.config.initialSize);
29895             }
29896         }
29897         this.fireEvent("paneladded", this, panel);
29898         return panel;
29899     },
29900     
29901     /**
29902      * Returns true if the panel is in this region.
29903      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29904      * @return {Boolean}
29905      */
29906     hasPanel : function(panel){
29907         if(typeof panel == "object"){ // must be panel obj
29908             panel = panel.getId();
29909         }
29910         return this.getPanel(panel) ? true : false;
29911     },
29912     
29913     /**
29914      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29915      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29916      * @param {Boolean} preservePanel Overrides the config preservePanel option
29917      * @return {Roo.ContentPanel} The panel that was removed
29918      */
29919     remove : function(panel, preservePanel){
29920         panel = this.getPanel(panel);
29921         if(!panel){
29922             return null;
29923         }
29924         var e = {};
29925         this.fireEvent("beforeremove", this, panel, e);
29926         if(e.cancel === true){
29927             return null;
29928         }
29929         var panelId = panel.getId();
29930         this.panels.removeKey(panelId);
29931         return panel;
29932     },
29933     
29934     /**
29935      * Returns the panel specified or null if it's not in this region.
29936      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29937      * @return {Roo.ContentPanel}
29938      */
29939     getPanel : function(id){
29940         if(typeof id == "object"){ // must be panel obj
29941             return id;
29942         }
29943         return this.panels.get(id);
29944     },
29945     
29946     /**
29947      * Returns this regions position (north/south/east/west/center).
29948      * @return {String} 
29949      */
29950     getPosition: function(){
29951         return this.position;    
29952     }
29953 });/*
29954  * Based on:
29955  * Ext JS Library 1.1.1
29956  * Copyright(c) 2006-2007, Ext JS, LLC.
29957  *
29958  * Originally Released Under LGPL - original licence link has changed is not relivant.
29959  *
29960  * Fork - LGPL
29961  * <script type="text/javascript">
29962  */
29963  
29964 /**
29965  * @class Roo.LayoutRegion
29966  * @extends Roo.BasicLayoutRegion
29967  * This class represents a region in a layout manager.
29968  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29969  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29970  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29971  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29972  * @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})
29973  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29974  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29975  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29976  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29977  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29978  * @cfg {String}    title           The title for the region (overrides panel titles)
29979  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29980  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29981  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29982  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29983  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29984  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29985  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29986  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29987  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29988  * @cfg {Boolean}   showPin         True to show a pin button
29989  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29990  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29991  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29992  * @cfg {Number}    width           For East/West panels
29993  * @cfg {Number}    height          For North/South panels
29994  * @cfg {Boolean}   split           To show the splitter
29995  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29996  */
29997 Roo.LayoutRegion = function(mgr, config, pos){
29998     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29999     var dh = Roo.DomHelper;
30000     /** This region's container element 
30001     * @type Roo.Element */
30002     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30003     /** This region's title element 
30004     * @type Roo.Element */
30005
30006     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30007         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30008         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30009     ]}, true);
30010     this.titleEl.enableDisplayMode();
30011     /** This region's title text element 
30012     * @type HTMLElement */
30013     this.titleTextEl = this.titleEl.dom.firstChild;
30014     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30015     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30016     this.closeBtn.enableDisplayMode();
30017     this.closeBtn.on("click", this.closeClicked, this);
30018     this.closeBtn.hide();
30019
30020     this.createBody(config);
30021     this.visible = true;
30022     this.collapsed = false;
30023
30024     if(config.hideWhenEmpty){
30025         this.hide();
30026         this.on("paneladded", this.validateVisibility, this);
30027         this.on("panelremoved", this.validateVisibility, this);
30028     }
30029     this.applyConfig(config);
30030 };
30031
30032 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30033
30034     createBody : function(){
30035         /** This region's body element 
30036         * @type Roo.Element */
30037         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30038     },
30039
30040     applyConfig : function(c){
30041         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30042             var dh = Roo.DomHelper;
30043             if(c.titlebar !== false){
30044                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30045                 this.collapseBtn.on("click", this.collapse, this);
30046                 this.collapseBtn.enableDisplayMode();
30047
30048                 if(c.showPin === true || this.showPin){
30049                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30050                     this.stickBtn.enableDisplayMode();
30051                     this.stickBtn.on("click", this.expand, this);
30052                     this.stickBtn.hide();
30053                 }
30054             }
30055             /** This region's collapsed element
30056             * @type Roo.Element */
30057             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30058                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30059             ]}, true);
30060             if(c.floatable !== false){
30061                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30062                this.collapsedEl.on("click", this.collapseClick, this);
30063             }
30064
30065             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30066                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30067                    id: "message", unselectable: "on", style:{"float":"left"}});
30068                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30069              }
30070             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30071             this.expandBtn.on("click", this.expand, this);
30072         }
30073         if(this.collapseBtn){
30074             this.collapseBtn.setVisible(c.collapsible == true);
30075         }
30076         this.cmargins = c.cmargins || this.cmargins ||
30077                          (this.position == "west" || this.position == "east" ?
30078                              {top: 0, left: 2, right:2, bottom: 0} :
30079                              {top: 2, left: 0, right:0, bottom: 2});
30080         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30081         this.bottomTabs = c.tabPosition != "top";
30082         this.autoScroll = c.autoScroll || false;
30083         if(this.autoScroll){
30084             this.bodyEl.setStyle("overflow", "auto");
30085         }else{
30086             this.bodyEl.setStyle("overflow", "hidden");
30087         }
30088         //if(c.titlebar !== false){
30089             if((!c.titlebar && !c.title) || c.titlebar === false){
30090                 this.titleEl.hide();
30091             }else{
30092                 this.titleEl.show();
30093                 if(c.title){
30094                     this.titleTextEl.innerHTML = c.title;
30095                 }
30096             }
30097         //}
30098         this.duration = c.duration || .30;
30099         this.slideDuration = c.slideDuration || .45;
30100         this.config = c;
30101         if(c.collapsed){
30102             this.collapse(true);
30103         }
30104         if(c.hidden){
30105             this.hide();
30106         }
30107     },
30108     /**
30109      * Returns true if this region is currently visible.
30110      * @return {Boolean}
30111      */
30112     isVisible : function(){
30113         return this.visible;
30114     },
30115
30116     /**
30117      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30118      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30119      */
30120     setCollapsedTitle : function(title){
30121         title = title || "&#160;";
30122         if(this.collapsedTitleTextEl){
30123             this.collapsedTitleTextEl.innerHTML = title;
30124         }
30125     },
30126
30127     getBox : function(){
30128         var b;
30129         if(!this.collapsed){
30130             b = this.el.getBox(false, true);
30131         }else{
30132             b = this.collapsedEl.getBox(false, true);
30133         }
30134         return b;
30135     },
30136
30137     getMargins : function(){
30138         return this.collapsed ? this.cmargins : this.margins;
30139     },
30140
30141     highlight : function(){
30142         this.el.addClass("x-layout-panel-dragover");
30143     },
30144
30145     unhighlight : function(){
30146         this.el.removeClass("x-layout-panel-dragover");
30147     },
30148
30149     updateBox : function(box){
30150         this.box = box;
30151         if(!this.collapsed){
30152             this.el.dom.style.left = box.x + "px";
30153             this.el.dom.style.top = box.y + "px";
30154             this.updateBody(box.width, box.height);
30155         }else{
30156             this.collapsedEl.dom.style.left = box.x + "px";
30157             this.collapsedEl.dom.style.top = box.y + "px";
30158             this.collapsedEl.setSize(box.width, box.height);
30159         }
30160         if(this.tabs){
30161             this.tabs.autoSizeTabs();
30162         }
30163     },
30164
30165     updateBody : function(w, h){
30166         if(w !== null){
30167             this.el.setWidth(w);
30168             w -= this.el.getBorderWidth("rl");
30169             if(this.config.adjustments){
30170                 w += this.config.adjustments[0];
30171             }
30172         }
30173         if(h !== null){
30174             this.el.setHeight(h);
30175             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30176             h -= this.el.getBorderWidth("tb");
30177             if(this.config.adjustments){
30178                 h += this.config.adjustments[1];
30179             }
30180             this.bodyEl.setHeight(h);
30181             if(this.tabs){
30182                 h = this.tabs.syncHeight(h);
30183             }
30184         }
30185         if(this.panelSize){
30186             w = w !== null ? w : this.panelSize.width;
30187             h = h !== null ? h : this.panelSize.height;
30188         }
30189         if(this.activePanel){
30190             var el = this.activePanel.getEl();
30191             w = w !== null ? w : el.getWidth();
30192             h = h !== null ? h : el.getHeight();
30193             this.panelSize = {width: w, height: h};
30194             this.activePanel.setSize(w, h);
30195         }
30196         if(Roo.isIE && this.tabs){
30197             this.tabs.el.repaint();
30198         }
30199     },
30200
30201     /**
30202      * Returns the container element for this region.
30203      * @return {Roo.Element}
30204      */
30205     getEl : function(){
30206         return this.el;
30207     },
30208
30209     /**
30210      * Hides this region.
30211      */
30212     hide : function(){
30213         if(!this.collapsed){
30214             this.el.dom.style.left = "-2000px";
30215             this.el.hide();
30216         }else{
30217             this.collapsedEl.dom.style.left = "-2000px";
30218             this.collapsedEl.hide();
30219         }
30220         this.visible = false;
30221         this.fireEvent("visibilitychange", this, false);
30222     },
30223
30224     /**
30225      * Shows this region if it was previously hidden.
30226      */
30227     show : function(){
30228         if(!this.collapsed){
30229             this.el.show();
30230         }else{
30231             this.collapsedEl.show();
30232         }
30233         this.visible = true;
30234         this.fireEvent("visibilitychange", this, true);
30235     },
30236
30237     closeClicked : function(){
30238         if(this.activePanel){
30239             this.remove(this.activePanel);
30240         }
30241     },
30242
30243     collapseClick : function(e){
30244         if(this.isSlid){
30245            e.stopPropagation();
30246            this.slideIn();
30247         }else{
30248            e.stopPropagation();
30249            this.slideOut();
30250         }
30251     },
30252
30253     /**
30254      * Collapses this region.
30255      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30256      */
30257     collapse : function(skipAnim, skipCheck){
30258         if(this.collapsed) {
30259             return;
30260         }
30261         
30262         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30263             
30264             this.collapsed = true;
30265             if(this.split){
30266                 this.split.el.hide();
30267             }
30268             if(this.config.animate && skipAnim !== true){
30269                 this.fireEvent("invalidated", this);
30270                 this.animateCollapse();
30271             }else{
30272                 this.el.setLocation(-20000,-20000);
30273                 this.el.hide();
30274                 this.collapsedEl.show();
30275                 this.fireEvent("collapsed", this);
30276                 this.fireEvent("invalidated", this);
30277             }
30278         }
30279         
30280     },
30281
30282     animateCollapse : function(){
30283         // overridden
30284     },
30285
30286     /**
30287      * Expands this region if it was previously collapsed.
30288      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30289      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30290      */
30291     expand : function(e, skipAnim){
30292         if(e) {
30293             e.stopPropagation();
30294         }
30295         if(!this.collapsed || this.el.hasActiveFx()) {
30296             return;
30297         }
30298         if(this.isSlid){
30299             this.afterSlideIn();
30300             skipAnim = true;
30301         }
30302         this.collapsed = false;
30303         if(this.config.animate && skipAnim !== true){
30304             this.animateExpand();
30305         }else{
30306             this.el.show();
30307             if(this.split){
30308                 this.split.el.show();
30309             }
30310             this.collapsedEl.setLocation(-2000,-2000);
30311             this.collapsedEl.hide();
30312             this.fireEvent("invalidated", this);
30313             this.fireEvent("expanded", this);
30314         }
30315     },
30316
30317     animateExpand : function(){
30318         // overridden
30319     },
30320
30321     initTabs : function()
30322     {
30323         this.bodyEl.setStyle("overflow", "hidden");
30324         var ts = new Roo.TabPanel(
30325                 this.bodyEl.dom,
30326                 {
30327                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30328                     disableTooltips: this.config.disableTabTips,
30329                     toolbar : this.config.toolbar
30330                 }
30331         );
30332         if(this.config.hideTabs){
30333             ts.stripWrap.setDisplayed(false);
30334         }
30335         this.tabs = ts;
30336         ts.resizeTabs = this.config.resizeTabs === true;
30337         ts.minTabWidth = this.config.minTabWidth || 40;
30338         ts.maxTabWidth = this.config.maxTabWidth || 250;
30339         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30340         ts.monitorResize = false;
30341         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30342         ts.bodyEl.addClass('x-layout-tabs-body');
30343         this.panels.each(this.initPanelAsTab, this);
30344     },
30345
30346     initPanelAsTab : function(panel){
30347         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30348                     this.config.closeOnTab && panel.isClosable());
30349         if(panel.tabTip !== undefined){
30350             ti.setTooltip(panel.tabTip);
30351         }
30352         ti.on("activate", function(){
30353               this.setActivePanel(panel);
30354         }, this);
30355         if(this.config.closeOnTab){
30356             ti.on("beforeclose", function(t, e){
30357                 e.cancel = true;
30358                 this.remove(panel);
30359             }, this);
30360         }
30361         return ti;
30362     },
30363
30364     updatePanelTitle : function(panel, title){
30365         if(this.activePanel == panel){
30366             this.updateTitle(title);
30367         }
30368         if(this.tabs){
30369             var ti = this.tabs.getTab(panel.getEl().id);
30370             ti.setText(title);
30371             if(panel.tabTip !== undefined){
30372                 ti.setTooltip(panel.tabTip);
30373             }
30374         }
30375     },
30376
30377     updateTitle : function(title){
30378         if(this.titleTextEl && !this.config.title){
30379             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30380         }
30381     },
30382
30383     setActivePanel : function(panel){
30384         panel = this.getPanel(panel);
30385         if(this.activePanel && this.activePanel != panel){
30386             this.activePanel.setActiveState(false);
30387         }
30388         this.activePanel = panel;
30389         panel.setActiveState(true);
30390         if(this.panelSize){
30391             panel.setSize(this.panelSize.width, this.panelSize.height);
30392         }
30393         if(this.closeBtn){
30394             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30395         }
30396         this.updateTitle(panel.getTitle());
30397         if(this.tabs){
30398             this.fireEvent("invalidated", this);
30399         }
30400         this.fireEvent("panelactivated", this, panel);
30401     },
30402
30403     /**
30404      * Shows the specified panel.
30405      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30406      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30407      */
30408     showPanel : function(panel)
30409     {
30410         panel = this.getPanel(panel);
30411         if(panel){
30412             if(this.tabs){
30413                 var tab = this.tabs.getTab(panel.getEl().id);
30414                 if(tab.isHidden()){
30415                     this.tabs.unhideTab(tab.id);
30416                 }
30417                 tab.activate();
30418             }else{
30419                 this.setActivePanel(panel);
30420             }
30421         }
30422         return panel;
30423     },
30424
30425     /**
30426      * Get the active panel for this region.
30427      * @return {Roo.ContentPanel} The active panel or null
30428      */
30429     getActivePanel : function(){
30430         return this.activePanel;
30431     },
30432
30433     validateVisibility : function(){
30434         if(this.panels.getCount() < 1){
30435             this.updateTitle("&#160;");
30436             this.closeBtn.hide();
30437             this.hide();
30438         }else{
30439             if(!this.isVisible()){
30440                 this.show();
30441             }
30442         }
30443     },
30444
30445     /**
30446      * Adds the passed ContentPanel(s) to this region.
30447      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30448      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30449      */
30450     add : function(panel){
30451         if(arguments.length > 1){
30452             for(var i = 0, len = arguments.length; i < len; i++) {
30453                 this.add(arguments[i]);
30454             }
30455             return null;
30456         }
30457         if(this.hasPanel(panel)){
30458             this.showPanel(panel);
30459             return panel;
30460         }
30461         panel.setRegion(this);
30462         this.panels.add(panel);
30463         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30464             this.bodyEl.dom.appendChild(panel.getEl().dom);
30465             if(panel.background !== true){
30466                 this.setActivePanel(panel);
30467             }
30468             this.fireEvent("paneladded", this, panel);
30469             return panel;
30470         }
30471         if(!this.tabs){
30472             this.initTabs();
30473         }else{
30474             this.initPanelAsTab(panel);
30475         }
30476         if(panel.background !== true){
30477             this.tabs.activate(panel.getEl().id);
30478         }
30479         this.fireEvent("paneladded", this, panel);
30480         return panel;
30481     },
30482
30483     /**
30484      * Hides the tab for the specified panel.
30485      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30486      */
30487     hidePanel : function(panel){
30488         if(this.tabs && (panel = this.getPanel(panel))){
30489             this.tabs.hideTab(panel.getEl().id);
30490         }
30491     },
30492
30493     /**
30494      * Unhides the tab for a previously hidden panel.
30495      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30496      */
30497     unhidePanel : function(panel){
30498         if(this.tabs && (panel = this.getPanel(panel))){
30499             this.tabs.unhideTab(panel.getEl().id);
30500         }
30501     },
30502
30503     clearPanels : function(){
30504         while(this.panels.getCount() > 0){
30505              this.remove(this.panels.first());
30506         }
30507     },
30508
30509     /**
30510      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30511      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30512      * @param {Boolean} preservePanel Overrides the config preservePanel option
30513      * @return {Roo.ContentPanel} The panel that was removed
30514      */
30515     remove : function(panel, preservePanel){
30516         panel = this.getPanel(panel);
30517         if(!panel){
30518             return null;
30519         }
30520         var e = {};
30521         this.fireEvent("beforeremove", this, panel, e);
30522         if(e.cancel === true){
30523             return null;
30524         }
30525         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30526         var panelId = panel.getId();
30527         this.panels.removeKey(panelId);
30528         if(preservePanel){
30529             document.body.appendChild(panel.getEl().dom);
30530         }
30531         if(this.tabs){
30532             this.tabs.removeTab(panel.getEl().id);
30533         }else if (!preservePanel){
30534             this.bodyEl.dom.removeChild(panel.getEl().dom);
30535         }
30536         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30537             var p = this.panels.first();
30538             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30539             tempEl.appendChild(p.getEl().dom);
30540             this.bodyEl.update("");
30541             this.bodyEl.dom.appendChild(p.getEl().dom);
30542             tempEl = null;
30543             this.updateTitle(p.getTitle());
30544             this.tabs = null;
30545             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30546             this.setActivePanel(p);
30547         }
30548         panel.setRegion(null);
30549         if(this.activePanel == panel){
30550             this.activePanel = null;
30551         }
30552         if(this.config.autoDestroy !== false && preservePanel !== true){
30553             try{panel.destroy();}catch(e){}
30554         }
30555         this.fireEvent("panelremoved", this, panel);
30556         return panel;
30557     },
30558
30559     /**
30560      * Returns the TabPanel component used by this region
30561      * @return {Roo.TabPanel}
30562      */
30563     getTabs : function(){
30564         return this.tabs;
30565     },
30566
30567     createTool : function(parentEl, className){
30568         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30569             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30570         btn.addClassOnOver("x-layout-tools-button-over");
30571         return btn;
30572     }
30573 });/*
30574  * Based on:
30575  * Ext JS Library 1.1.1
30576  * Copyright(c) 2006-2007, Ext JS, LLC.
30577  *
30578  * Originally Released Under LGPL - original licence link has changed is not relivant.
30579  *
30580  * Fork - LGPL
30581  * <script type="text/javascript">
30582  */
30583  
30584
30585
30586 /**
30587  * @class Roo.SplitLayoutRegion
30588  * @extends Roo.LayoutRegion
30589  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30590  */
30591 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30592     this.cursor = cursor;
30593     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30594 };
30595
30596 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30597     splitTip : "Drag to resize.",
30598     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30599     useSplitTips : false,
30600
30601     applyConfig : function(config){
30602         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30603         if(config.split){
30604             if(!this.split){
30605                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30606                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30607                 /** The SplitBar for this region 
30608                 * @type Roo.SplitBar */
30609                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30610                 this.split.on("moved", this.onSplitMove, this);
30611                 this.split.useShim = config.useShim === true;
30612                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30613                 if(this.useSplitTips){
30614                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30615                 }
30616                 if(config.collapsible){
30617                     this.split.el.on("dblclick", this.collapse,  this);
30618                 }
30619             }
30620             if(typeof config.minSize != "undefined"){
30621                 this.split.minSize = config.minSize;
30622             }
30623             if(typeof config.maxSize != "undefined"){
30624                 this.split.maxSize = config.maxSize;
30625             }
30626             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30627                 this.hideSplitter();
30628             }
30629         }
30630     },
30631
30632     getHMaxSize : function(){
30633          var cmax = this.config.maxSize || 10000;
30634          var center = this.mgr.getRegion("center");
30635          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30636     },
30637
30638     getVMaxSize : function(){
30639          var cmax = this.config.maxSize || 10000;
30640          var center = this.mgr.getRegion("center");
30641          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30642     },
30643
30644     onSplitMove : function(split, newSize){
30645         this.fireEvent("resized", this, newSize);
30646     },
30647     
30648     /** 
30649      * Returns the {@link Roo.SplitBar} for this region.
30650      * @return {Roo.SplitBar}
30651      */
30652     getSplitBar : function(){
30653         return this.split;
30654     },
30655     
30656     hide : function(){
30657         this.hideSplitter();
30658         Roo.SplitLayoutRegion.superclass.hide.call(this);
30659     },
30660
30661     hideSplitter : function(){
30662         if(this.split){
30663             this.split.el.setLocation(-2000,-2000);
30664             this.split.el.hide();
30665         }
30666     },
30667
30668     show : function(){
30669         if(this.split){
30670             this.split.el.show();
30671         }
30672         Roo.SplitLayoutRegion.superclass.show.call(this);
30673     },
30674     
30675     beforeSlide: function(){
30676         if(Roo.isGecko){// firefox overflow auto bug workaround
30677             this.bodyEl.clip();
30678             if(this.tabs) {
30679                 this.tabs.bodyEl.clip();
30680             }
30681             if(this.activePanel){
30682                 this.activePanel.getEl().clip();
30683                 
30684                 if(this.activePanel.beforeSlide){
30685                     this.activePanel.beforeSlide();
30686                 }
30687             }
30688         }
30689     },
30690     
30691     afterSlide : function(){
30692         if(Roo.isGecko){// firefox overflow auto bug workaround
30693             this.bodyEl.unclip();
30694             if(this.tabs) {
30695                 this.tabs.bodyEl.unclip();
30696             }
30697             if(this.activePanel){
30698                 this.activePanel.getEl().unclip();
30699                 if(this.activePanel.afterSlide){
30700                     this.activePanel.afterSlide();
30701                 }
30702             }
30703         }
30704     },
30705
30706     initAutoHide : function(){
30707         if(this.autoHide !== false){
30708             if(!this.autoHideHd){
30709                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30710                 this.autoHideHd = {
30711                     "mouseout": function(e){
30712                         if(!e.within(this.el, true)){
30713                             st.delay(500);
30714                         }
30715                     },
30716                     "mouseover" : function(e){
30717                         st.cancel();
30718                     },
30719                     scope : this
30720                 };
30721             }
30722             this.el.on(this.autoHideHd);
30723         }
30724     },
30725
30726     clearAutoHide : function(){
30727         if(this.autoHide !== false){
30728             this.el.un("mouseout", this.autoHideHd.mouseout);
30729             this.el.un("mouseover", this.autoHideHd.mouseover);
30730         }
30731     },
30732
30733     clearMonitor : function(){
30734         Roo.get(document).un("click", this.slideInIf, this);
30735     },
30736
30737     // these names are backwards but not changed for compat
30738     slideOut : function(){
30739         if(this.isSlid || this.el.hasActiveFx()){
30740             return;
30741         }
30742         this.isSlid = true;
30743         if(this.collapseBtn){
30744             this.collapseBtn.hide();
30745         }
30746         this.closeBtnState = this.closeBtn.getStyle('display');
30747         this.closeBtn.hide();
30748         if(this.stickBtn){
30749             this.stickBtn.show();
30750         }
30751         this.el.show();
30752         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30753         this.beforeSlide();
30754         this.el.setStyle("z-index", 10001);
30755         this.el.slideIn(this.getSlideAnchor(), {
30756             callback: function(){
30757                 this.afterSlide();
30758                 this.initAutoHide();
30759                 Roo.get(document).on("click", this.slideInIf, this);
30760                 this.fireEvent("slideshow", this);
30761             },
30762             scope: this,
30763             block: true
30764         });
30765     },
30766
30767     afterSlideIn : function(){
30768         this.clearAutoHide();
30769         this.isSlid = false;
30770         this.clearMonitor();
30771         this.el.setStyle("z-index", "");
30772         if(this.collapseBtn){
30773             this.collapseBtn.show();
30774         }
30775         this.closeBtn.setStyle('display', this.closeBtnState);
30776         if(this.stickBtn){
30777             this.stickBtn.hide();
30778         }
30779         this.fireEvent("slidehide", this);
30780     },
30781
30782     slideIn : function(cb){
30783         if(!this.isSlid || this.el.hasActiveFx()){
30784             Roo.callback(cb);
30785             return;
30786         }
30787         this.isSlid = false;
30788         this.beforeSlide();
30789         this.el.slideOut(this.getSlideAnchor(), {
30790             callback: function(){
30791                 this.el.setLeftTop(-10000, -10000);
30792                 this.afterSlide();
30793                 this.afterSlideIn();
30794                 Roo.callback(cb);
30795             },
30796             scope: this,
30797             block: true
30798         });
30799     },
30800     
30801     slideInIf : function(e){
30802         if(!e.within(this.el)){
30803             this.slideIn();
30804         }
30805     },
30806
30807     animateCollapse : function(){
30808         this.beforeSlide();
30809         this.el.setStyle("z-index", 20000);
30810         var anchor = this.getSlideAnchor();
30811         this.el.slideOut(anchor, {
30812             callback : function(){
30813                 this.el.setStyle("z-index", "");
30814                 this.collapsedEl.slideIn(anchor, {duration:.3});
30815                 this.afterSlide();
30816                 this.el.setLocation(-10000,-10000);
30817                 this.el.hide();
30818                 this.fireEvent("collapsed", this);
30819             },
30820             scope: this,
30821             block: true
30822         });
30823     },
30824
30825     animateExpand : function(){
30826         this.beforeSlide();
30827         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30828         this.el.setStyle("z-index", 20000);
30829         this.collapsedEl.hide({
30830             duration:.1
30831         });
30832         this.el.slideIn(this.getSlideAnchor(), {
30833             callback : function(){
30834                 this.el.setStyle("z-index", "");
30835                 this.afterSlide();
30836                 if(this.split){
30837                     this.split.el.show();
30838                 }
30839                 this.fireEvent("invalidated", this);
30840                 this.fireEvent("expanded", this);
30841             },
30842             scope: this,
30843             block: true
30844         });
30845     },
30846
30847     anchors : {
30848         "west" : "left",
30849         "east" : "right",
30850         "north" : "top",
30851         "south" : "bottom"
30852     },
30853
30854     sanchors : {
30855         "west" : "l",
30856         "east" : "r",
30857         "north" : "t",
30858         "south" : "b"
30859     },
30860
30861     canchors : {
30862         "west" : "tl-tr",
30863         "east" : "tr-tl",
30864         "north" : "tl-bl",
30865         "south" : "bl-tl"
30866     },
30867
30868     getAnchor : function(){
30869         return this.anchors[this.position];
30870     },
30871
30872     getCollapseAnchor : function(){
30873         return this.canchors[this.position];
30874     },
30875
30876     getSlideAnchor : function(){
30877         return this.sanchors[this.position];
30878     },
30879
30880     getAlignAdj : function(){
30881         var cm = this.cmargins;
30882         switch(this.position){
30883             case "west":
30884                 return [0, 0];
30885             break;
30886             case "east":
30887                 return [0, 0];
30888             break;
30889             case "north":
30890                 return [0, 0];
30891             break;
30892             case "south":
30893                 return [0, 0];
30894             break;
30895         }
30896     },
30897
30898     getExpandAdj : function(){
30899         var c = this.collapsedEl, cm = this.cmargins;
30900         switch(this.position){
30901             case "west":
30902                 return [-(cm.right+c.getWidth()+cm.left), 0];
30903             break;
30904             case "east":
30905                 return [cm.right+c.getWidth()+cm.left, 0];
30906             break;
30907             case "north":
30908                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30909             break;
30910             case "south":
30911                 return [0, cm.top+cm.bottom+c.getHeight()];
30912             break;
30913         }
30914     }
30915 });/*
30916  * Based on:
30917  * Ext JS Library 1.1.1
30918  * Copyright(c) 2006-2007, Ext JS, LLC.
30919  *
30920  * Originally Released Under LGPL - original licence link has changed is not relivant.
30921  *
30922  * Fork - LGPL
30923  * <script type="text/javascript">
30924  */
30925 /*
30926  * These classes are private internal classes
30927  */
30928 Roo.CenterLayoutRegion = function(mgr, config){
30929     Roo.LayoutRegion.call(this, mgr, config, "center");
30930     this.visible = true;
30931     this.minWidth = config.minWidth || 20;
30932     this.minHeight = config.minHeight || 20;
30933 };
30934
30935 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30936     hide : function(){
30937         // center panel can't be hidden
30938     },
30939     
30940     show : function(){
30941         // center panel can't be hidden
30942     },
30943     
30944     getMinWidth: function(){
30945         return this.minWidth;
30946     },
30947     
30948     getMinHeight: function(){
30949         return this.minHeight;
30950     }
30951 });
30952
30953
30954 Roo.NorthLayoutRegion = function(mgr, config){
30955     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30956     if(this.split){
30957         this.split.placement = Roo.SplitBar.TOP;
30958         this.split.orientation = Roo.SplitBar.VERTICAL;
30959         this.split.el.addClass("x-layout-split-v");
30960     }
30961     var size = config.initialSize || config.height;
30962     if(typeof size != "undefined"){
30963         this.el.setHeight(size);
30964     }
30965 };
30966 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30967     orientation: Roo.SplitBar.VERTICAL,
30968     getBox : function(){
30969         if(this.collapsed){
30970             return this.collapsedEl.getBox();
30971         }
30972         var box = this.el.getBox();
30973         if(this.split){
30974             box.height += this.split.el.getHeight();
30975         }
30976         return box;
30977     },
30978     
30979     updateBox : function(box){
30980         if(this.split && !this.collapsed){
30981             box.height -= this.split.el.getHeight();
30982             this.split.el.setLeft(box.x);
30983             this.split.el.setTop(box.y+box.height);
30984             this.split.el.setWidth(box.width);
30985         }
30986         if(this.collapsed){
30987             this.updateBody(box.width, null);
30988         }
30989         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30990     }
30991 });
30992
30993 Roo.SouthLayoutRegion = function(mgr, config){
30994     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30995     if(this.split){
30996         this.split.placement = Roo.SplitBar.BOTTOM;
30997         this.split.orientation = Roo.SplitBar.VERTICAL;
30998         this.split.el.addClass("x-layout-split-v");
30999     }
31000     var size = config.initialSize || config.height;
31001     if(typeof size != "undefined"){
31002         this.el.setHeight(size);
31003     }
31004 };
31005 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31006     orientation: Roo.SplitBar.VERTICAL,
31007     getBox : function(){
31008         if(this.collapsed){
31009             return this.collapsedEl.getBox();
31010         }
31011         var box = this.el.getBox();
31012         if(this.split){
31013             var sh = this.split.el.getHeight();
31014             box.height += sh;
31015             box.y -= sh;
31016         }
31017         return box;
31018     },
31019     
31020     updateBox : function(box){
31021         if(this.split && !this.collapsed){
31022             var sh = this.split.el.getHeight();
31023             box.height -= sh;
31024             box.y += sh;
31025             this.split.el.setLeft(box.x);
31026             this.split.el.setTop(box.y-sh);
31027             this.split.el.setWidth(box.width);
31028         }
31029         if(this.collapsed){
31030             this.updateBody(box.width, null);
31031         }
31032         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31033     }
31034 });
31035
31036 Roo.EastLayoutRegion = function(mgr, config){
31037     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31038     if(this.split){
31039         this.split.placement = Roo.SplitBar.RIGHT;
31040         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31041         this.split.el.addClass("x-layout-split-h");
31042     }
31043     var size = config.initialSize || config.width;
31044     if(typeof size != "undefined"){
31045         this.el.setWidth(size);
31046     }
31047 };
31048 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31049     orientation: Roo.SplitBar.HORIZONTAL,
31050     getBox : function(){
31051         if(this.collapsed){
31052             return this.collapsedEl.getBox();
31053         }
31054         var box = this.el.getBox();
31055         if(this.split){
31056             var sw = this.split.el.getWidth();
31057             box.width += sw;
31058             box.x -= sw;
31059         }
31060         return box;
31061     },
31062
31063     updateBox : function(box){
31064         if(this.split && !this.collapsed){
31065             var sw = this.split.el.getWidth();
31066             box.width -= sw;
31067             this.split.el.setLeft(box.x);
31068             this.split.el.setTop(box.y);
31069             this.split.el.setHeight(box.height);
31070             box.x += sw;
31071         }
31072         if(this.collapsed){
31073             this.updateBody(null, box.height);
31074         }
31075         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31076     }
31077 });
31078
31079 Roo.WestLayoutRegion = function(mgr, config){
31080     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31081     if(this.split){
31082         this.split.placement = Roo.SplitBar.LEFT;
31083         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31084         this.split.el.addClass("x-layout-split-h");
31085     }
31086     var size = config.initialSize || config.width;
31087     if(typeof size != "undefined"){
31088         this.el.setWidth(size);
31089     }
31090 };
31091 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31092     orientation: Roo.SplitBar.HORIZONTAL,
31093     getBox : function(){
31094         if(this.collapsed){
31095             return this.collapsedEl.getBox();
31096         }
31097         var box = this.el.getBox();
31098         if(this.split){
31099             box.width += this.split.el.getWidth();
31100         }
31101         return box;
31102     },
31103     
31104     updateBox : function(box){
31105         if(this.split && !this.collapsed){
31106             var sw = this.split.el.getWidth();
31107             box.width -= sw;
31108             this.split.el.setLeft(box.x+box.width);
31109             this.split.el.setTop(box.y);
31110             this.split.el.setHeight(box.height);
31111         }
31112         if(this.collapsed){
31113             this.updateBody(null, box.height);
31114         }
31115         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31116     }
31117 });
31118 /*
31119  * Based on:
31120  * Ext JS Library 1.1.1
31121  * Copyright(c) 2006-2007, Ext JS, LLC.
31122  *
31123  * Originally Released Under LGPL - original licence link has changed is not relivant.
31124  *
31125  * Fork - LGPL
31126  * <script type="text/javascript">
31127  */
31128  
31129  
31130 /*
31131  * Private internal class for reading and applying state
31132  */
31133 Roo.LayoutStateManager = function(layout){
31134      // default empty state
31135      this.state = {
31136         north: {},
31137         south: {},
31138         east: {},
31139         west: {}       
31140     };
31141 };
31142
31143 Roo.LayoutStateManager.prototype = {
31144     init : function(layout, provider){
31145         this.provider = provider;
31146         var state = provider.get(layout.id+"-layout-state");
31147         if(state){
31148             var wasUpdating = layout.isUpdating();
31149             if(!wasUpdating){
31150                 layout.beginUpdate();
31151             }
31152             for(var key in state){
31153                 if(typeof state[key] != "function"){
31154                     var rstate = state[key];
31155                     var r = layout.getRegion(key);
31156                     if(r && rstate){
31157                         if(rstate.size){
31158                             r.resizeTo(rstate.size);
31159                         }
31160                         if(rstate.collapsed == true){
31161                             r.collapse(true);
31162                         }else{
31163                             r.expand(null, true);
31164                         }
31165                     }
31166                 }
31167             }
31168             if(!wasUpdating){
31169                 layout.endUpdate();
31170             }
31171             this.state = state; 
31172         }
31173         this.layout = layout;
31174         layout.on("regionresized", this.onRegionResized, this);
31175         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31176         layout.on("regionexpanded", this.onRegionExpanded, this);
31177     },
31178     
31179     storeState : function(){
31180         this.provider.set(this.layout.id+"-layout-state", this.state);
31181     },
31182     
31183     onRegionResized : function(region, newSize){
31184         this.state[region.getPosition()].size = newSize;
31185         this.storeState();
31186     },
31187     
31188     onRegionCollapsed : function(region){
31189         this.state[region.getPosition()].collapsed = true;
31190         this.storeState();
31191     },
31192     
31193     onRegionExpanded : function(region){
31194         this.state[region.getPosition()].collapsed = false;
31195         this.storeState();
31196     }
31197 };/*
31198  * Based on:
31199  * Ext JS Library 1.1.1
31200  * Copyright(c) 2006-2007, Ext JS, LLC.
31201  *
31202  * Originally Released Under LGPL - original licence link has changed is not relivant.
31203  *
31204  * Fork - LGPL
31205  * <script type="text/javascript">
31206  */
31207 /**
31208  * @class Roo.ContentPanel
31209  * @extends Roo.util.Observable
31210  * A basic ContentPanel element.
31211  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31212  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31213  * @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
31214  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31215  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31216  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31217  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31218  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31219  * @cfg {String} title          The title for this panel
31220  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31221  * @cfg {String} url            Calls {@link #setUrl} with this value
31222  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31223  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31224  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31225  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31226  * @cfg {String}    style  Extra style to add to the content panel 
31227
31228  * @constructor
31229  * Create a new ContentPanel.
31230  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31231  * @param {String/Object} config A string to set only the title or a config object
31232  * @param {String} content (optional) Set the HTML content for this panel
31233  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31234  */
31235 Roo.ContentPanel = function(el, config, content){
31236     
31237      
31238     /*
31239     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31240         config = el;
31241         el = Roo.id();
31242     }
31243     if (config && config.parentLayout) { 
31244         el = config.parentLayout.el.createChild(); 
31245     }
31246     */
31247     if(el.autoCreate){ // xtype is available if this is called from factory
31248         config = el;
31249         el = Roo.id();
31250     }
31251     this.el = Roo.get(el);
31252     if(!this.el && config && config.autoCreate){
31253         if(typeof config.autoCreate == "object"){
31254             if(!config.autoCreate.id){
31255                 config.autoCreate.id = config.id||el;
31256             }
31257             this.el = Roo.DomHelper.append(document.body,
31258                         config.autoCreate, true);
31259         }else{
31260             this.el = Roo.DomHelper.append(document.body,
31261                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31262         }
31263     }
31264     
31265     
31266     this.closable = false;
31267     this.loaded = false;
31268     this.active = false;
31269     if(typeof config == "string"){
31270         this.title = config;
31271     }else{
31272         Roo.apply(this, config);
31273     }
31274     
31275     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31276         this.wrapEl = this.el.wrap();
31277         this.toolbar.container = this.el.insertSibling(false, 'before');
31278         this.toolbar = new Roo.Toolbar(this.toolbar);
31279     }
31280     
31281     // xtype created footer. - not sure if will work as we normally have to render first..
31282     if (this.footer && !this.footer.el && this.footer.xtype) {
31283         if (!this.wrapEl) {
31284             this.wrapEl = this.el.wrap();
31285         }
31286     
31287         this.footer.container = this.wrapEl.createChild();
31288          
31289         this.footer = Roo.factory(this.footer, Roo);
31290         
31291     }
31292     
31293     if(this.resizeEl){
31294         this.resizeEl = Roo.get(this.resizeEl, true);
31295     }else{
31296         this.resizeEl = this.el;
31297     }
31298     // handle view.xtype
31299     
31300  
31301     
31302     
31303     this.addEvents({
31304         /**
31305          * @event activate
31306          * Fires when this panel is activated. 
31307          * @param {Roo.ContentPanel} this
31308          */
31309         "activate" : true,
31310         /**
31311          * @event deactivate
31312          * Fires when this panel is activated. 
31313          * @param {Roo.ContentPanel} this
31314          */
31315         "deactivate" : true,
31316
31317         /**
31318          * @event resize
31319          * Fires when this panel is resized if fitToFrame is true.
31320          * @param {Roo.ContentPanel} this
31321          * @param {Number} width The width after any component adjustments
31322          * @param {Number} height The height after any component adjustments
31323          */
31324         "resize" : true,
31325         
31326          /**
31327          * @event render
31328          * Fires when this tab is created
31329          * @param {Roo.ContentPanel} this
31330          */
31331         "render" : true
31332          
31333         
31334     });
31335     
31336
31337     
31338     
31339     if(this.autoScroll){
31340         this.resizeEl.setStyle("overflow", "auto");
31341     } else {
31342         // fix randome scrolling
31343         this.el.on('scroll', function() {
31344             Roo.log('fix random scolling');
31345             this.scrollTo('top',0); 
31346         });
31347     }
31348     content = content || this.content;
31349     if(content){
31350         this.setContent(content);
31351     }
31352     if(config && config.url){
31353         this.setUrl(this.url, this.params, this.loadOnce);
31354     }
31355     
31356     
31357     
31358     Roo.ContentPanel.superclass.constructor.call(this);
31359     
31360     if (this.view && typeof(this.view.xtype) != 'undefined') {
31361         this.view.el = this.el.appendChild(document.createElement("div"));
31362         this.view = Roo.factory(this.view); 
31363         this.view.render  &&  this.view.render(false, '');  
31364     }
31365     
31366     
31367     this.fireEvent('render', this);
31368 };
31369
31370 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31371     tabTip:'',
31372     setRegion : function(region){
31373         this.region = region;
31374         if(region){
31375            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31376         }else{
31377            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31378         } 
31379     },
31380     
31381     /**
31382      * Returns the toolbar for this Panel if one was configured. 
31383      * @return {Roo.Toolbar} 
31384      */
31385     getToolbar : function(){
31386         return this.toolbar;
31387     },
31388     
31389     setActiveState : function(active){
31390         this.active = active;
31391         if(!active){
31392             this.fireEvent("deactivate", this);
31393         }else{
31394             this.fireEvent("activate", this);
31395         }
31396     },
31397     /**
31398      * Updates this panel's element
31399      * @param {String} content The new content
31400      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31401     */
31402     setContent : function(content, loadScripts){
31403         this.el.update(content, loadScripts);
31404     },
31405
31406     ignoreResize : function(w, h){
31407         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31408             return true;
31409         }else{
31410             this.lastSize = {width: w, height: h};
31411             return false;
31412         }
31413     },
31414     /**
31415      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31416      * @return {Roo.UpdateManager} The UpdateManager
31417      */
31418     getUpdateManager : function(){
31419         return this.el.getUpdateManager();
31420     },
31421      /**
31422      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31423      * @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:
31424 <pre><code>
31425 panel.load({
31426     url: "your-url.php",
31427     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31428     callback: yourFunction,
31429     scope: yourObject, //(optional scope)
31430     discardUrl: false,
31431     nocache: false,
31432     text: "Loading...",
31433     timeout: 30,
31434     scripts: false
31435 });
31436 </code></pre>
31437      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31438      * 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.
31439      * @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}
31440      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31441      * @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.
31442      * @return {Roo.ContentPanel} this
31443      */
31444     load : function(){
31445         var um = this.el.getUpdateManager();
31446         um.update.apply(um, arguments);
31447         return this;
31448     },
31449
31450
31451     /**
31452      * 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.
31453      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31454      * @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)
31455      * @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)
31456      * @return {Roo.UpdateManager} The UpdateManager
31457      */
31458     setUrl : function(url, params, loadOnce){
31459         if(this.refreshDelegate){
31460             this.removeListener("activate", this.refreshDelegate);
31461         }
31462         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31463         this.on("activate", this.refreshDelegate);
31464         return this.el.getUpdateManager();
31465     },
31466     
31467     _handleRefresh : function(url, params, loadOnce){
31468         if(!loadOnce || !this.loaded){
31469             var updater = this.el.getUpdateManager();
31470             updater.update(url, params, this._setLoaded.createDelegate(this));
31471         }
31472     },
31473     
31474     _setLoaded : function(){
31475         this.loaded = true;
31476     }, 
31477     
31478     /**
31479      * Returns this panel's id
31480      * @return {String} 
31481      */
31482     getId : function(){
31483         return this.el.id;
31484     },
31485     
31486     /** 
31487      * Returns this panel's element - used by regiosn to add.
31488      * @return {Roo.Element} 
31489      */
31490     getEl : function(){
31491         return this.wrapEl || this.el;
31492     },
31493     
31494     adjustForComponents : function(width, height)
31495     {
31496         //Roo.log('adjustForComponents ');
31497         if(this.resizeEl != this.el){
31498             width -= this.el.getFrameWidth('lr');
31499             height -= this.el.getFrameWidth('tb');
31500         }
31501         if(this.toolbar){
31502             var te = this.toolbar.getEl();
31503             height -= te.getHeight();
31504             te.setWidth(width);
31505         }
31506         if(this.footer){
31507             var te = this.footer.getEl();
31508             //Roo.log("footer:" + te.getHeight());
31509             
31510             height -= te.getHeight();
31511             te.setWidth(width);
31512         }
31513         
31514         
31515         if(this.adjustments){
31516             width += this.adjustments[0];
31517             height += this.adjustments[1];
31518         }
31519         return {"width": width, "height": height};
31520     },
31521     
31522     setSize : function(width, height){
31523         if(this.fitToFrame && !this.ignoreResize(width, height)){
31524             if(this.fitContainer && this.resizeEl != this.el){
31525                 this.el.setSize(width, height);
31526             }
31527             var size = this.adjustForComponents(width, height);
31528             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31529             this.fireEvent('resize', this, size.width, size.height);
31530         }
31531     },
31532     
31533     /**
31534      * Returns this panel's title
31535      * @return {String} 
31536      */
31537     getTitle : function(){
31538         return this.title;
31539     },
31540     
31541     /**
31542      * Set this panel's title
31543      * @param {String} title
31544      */
31545     setTitle : function(title){
31546         this.title = title;
31547         if(this.region){
31548             this.region.updatePanelTitle(this, title);
31549         }
31550     },
31551     
31552     /**
31553      * Returns true is this panel was configured to be closable
31554      * @return {Boolean} 
31555      */
31556     isClosable : function(){
31557         return this.closable;
31558     },
31559     
31560     beforeSlide : function(){
31561         this.el.clip();
31562         this.resizeEl.clip();
31563     },
31564     
31565     afterSlide : function(){
31566         this.el.unclip();
31567         this.resizeEl.unclip();
31568     },
31569     
31570     /**
31571      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31572      *   Will fail silently if the {@link #setUrl} method has not been called.
31573      *   This does not activate the panel, just updates its content.
31574      */
31575     refresh : function(){
31576         if(this.refreshDelegate){
31577            this.loaded = false;
31578            this.refreshDelegate();
31579         }
31580     },
31581     
31582     /**
31583      * Destroys this panel
31584      */
31585     destroy : function(){
31586         this.el.removeAllListeners();
31587         var tempEl = document.createElement("span");
31588         tempEl.appendChild(this.el.dom);
31589         tempEl.innerHTML = "";
31590         this.el.remove();
31591         this.el = null;
31592     },
31593     
31594     /**
31595      * form - if the content panel contains a form - this is a reference to it.
31596      * @type {Roo.form.Form}
31597      */
31598     form : false,
31599     /**
31600      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31601      *    This contains a reference to it.
31602      * @type {Roo.View}
31603      */
31604     view : false,
31605     
31606       /**
31607      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31608      * <pre><code>
31609
31610 layout.addxtype({
31611        xtype : 'Form',
31612        items: [ .... ]
31613    }
31614 );
31615
31616 </code></pre>
31617      * @param {Object} cfg Xtype definition of item to add.
31618      */
31619     
31620     addxtype : function(cfg) {
31621         // add form..
31622         if (cfg.xtype.match(/^Form$/)) {
31623             
31624             var el;
31625             //if (this.footer) {
31626             //    el = this.footer.container.insertSibling(false, 'before');
31627             //} else {
31628                 el = this.el.createChild();
31629             //}
31630
31631             this.form = new  Roo.form.Form(cfg);
31632             
31633             
31634             if ( this.form.allItems.length) {
31635                 this.form.render(el.dom);
31636             }
31637             return this.form;
31638         }
31639         // should only have one of theses..
31640         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31641             // views.. should not be just added - used named prop 'view''
31642             
31643             cfg.el = this.el.appendChild(document.createElement("div"));
31644             // factory?
31645             
31646             var ret = new Roo.factory(cfg);
31647              
31648              ret.render && ret.render(false, ''); // render blank..
31649             this.view = ret;
31650             return ret;
31651         }
31652         return false;
31653     }
31654 });
31655
31656 /**
31657  * @class Roo.GridPanel
31658  * @extends Roo.ContentPanel
31659  * @constructor
31660  * Create a new GridPanel.
31661  * @param {Roo.grid.Grid} grid The grid for this panel
31662  * @param {String/Object} config A string to set only the panel's title, or a config object
31663  */
31664 Roo.GridPanel = function(grid, config){
31665     
31666   
31667     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31668         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31669         
31670     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31671     
31672     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31673     
31674     if(this.toolbar){
31675         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31676     }
31677     // xtype created footer. - not sure if will work as we normally have to render first..
31678     if (this.footer && !this.footer.el && this.footer.xtype) {
31679         
31680         this.footer.container = this.grid.getView().getFooterPanel(true);
31681         this.footer.dataSource = this.grid.dataSource;
31682         this.footer = Roo.factory(this.footer, Roo);
31683         
31684     }
31685     
31686     grid.monitorWindowResize = false; // turn off autosizing
31687     grid.autoHeight = false;
31688     grid.autoWidth = false;
31689     this.grid = grid;
31690     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31691 };
31692
31693 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31694     getId : function(){
31695         return this.grid.id;
31696     },
31697     
31698     /**
31699      * Returns the grid for this panel
31700      * @return {Roo.grid.Grid} 
31701      */
31702     getGrid : function(){
31703         return this.grid;    
31704     },
31705     
31706     setSize : function(width, height){
31707         if(!this.ignoreResize(width, height)){
31708             var grid = this.grid;
31709             var size = this.adjustForComponents(width, height);
31710             grid.getGridEl().setSize(size.width, size.height);
31711             grid.autoSize();
31712         }
31713     },
31714     
31715     beforeSlide : function(){
31716         this.grid.getView().scroller.clip();
31717     },
31718     
31719     afterSlide : function(){
31720         this.grid.getView().scroller.unclip();
31721     },
31722     
31723     destroy : function(){
31724         this.grid.destroy();
31725         delete this.grid;
31726         Roo.GridPanel.superclass.destroy.call(this); 
31727     }
31728 });
31729
31730
31731 /**
31732  * @class Roo.NestedLayoutPanel
31733  * @extends Roo.ContentPanel
31734  * @constructor
31735  * Create a new NestedLayoutPanel.
31736  * 
31737  * 
31738  * @param {Roo.BorderLayout} layout The layout for this panel
31739  * @param {String/Object} config A string to set only the title or a config object
31740  */
31741 Roo.NestedLayoutPanel = function(layout, config)
31742 {
31743     // construct with only one argument..
31744     /* FIXME - implement nicer consturctors
31745     if (layout.layout) {
31746         config = layout;
31747         layout = config.layout;
31748         delete config.layout;
31749     }
31750     if (layout.xtype && !layout.getEl) {
31751         // then layout needs constructing..
31752         layout = Roo.factory(layout, Roo);
31753     }
31754     */
31755     
31756     
31757     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31758     
31759     layout.monitorWindowResize = false; // turn off autosizing
31760     this.layout = layout;
31761     this.layout.getEl().addClass("x-layout-nested-layout");
31762     
31763     
31764     
31765     
31766 };
31767
31768 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31769
31770     setSize : function(width, height){
31771         if(!this.ignoreResize(width, height)){
31772             var size = this.adjustForComponents(width, height);
31773             var el = this.layout.getEl();
31774             el.setSize(size.width, size.height);
31775             var touch = el.dom.offsetWidth;
31776             this.layout.layout();
31777             // ie requires a double layout on the first pass
31778             if(Roo.isIE && !this.initialized){
31779                 this.initialized = true;
31780                 this.layout.layout();
31781             }
31782         }
31783     },
31784     
31785     // activate all subpanels if not currently active..
31786     
31787     setActiveState : function(active){
31788         this.active = active;
31789         if(!active){
31790             this.fireEvent("deactivate", this);
31791             return;
31792         }
31793         
31794         this.fireEvent("activate", this);
31795         // not sure if this should happen before or after..
31796         if (!this.layout) {
31797             return; // should not happen..
31798         }
31799         var reg = false;
31800         for (var r in this.layout.regions) {
31801             reg = this.layout.getRegion(r);
31802             if (reg.getActivePanel()) {
31803                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31804                 reg.setActivePanel(reg.getActivePanel());
31805                 continue;
31806             }
31807             if (!reg.panels.length) {
31808                 continue;
31809             }
31810             reg.showPanel(reg.getPanel(0));
31811         }
31812         
31813         
31814         
31815         
31816     },
31817     
31818     /**
31819      * Returns the nested BorderLayout for this panel
31820      * @return {Roo.BorderLayout} 
31821      */
31822     getLayout : function(){
31823         return this.layout;
31824     },
31825     
31826      /**
31827      * Adds a xtype elements to the layout of the nested panel
31828      * <pre><code>
31829
31830 panel.addxtype({
31831        xtype : 'ContentPanel',
31832        region: 'west',
31833        items: [ .... ]
31834    }
31835 );
31836
31837 panel.addxtype({
31838         xtype : 'NestedLayoutPanel',
31839         region: 'west',
31840         layout: {
31841            center: { },
31842            west: { }   
31843         },
31844         items : [ ... list of content panels or nested layout panels.. ]
31845    }
31846 );
31847 </code></pre>
31848      * @param {Object} cfg Xtype definition of item to add.
31849      */
31850     addxtype : function(cfg) {
31851         return this.layout.addxtype(cfg);
31852     
31853     }
31854 });
31855
31856 Roo.ScrollPanel = function(el, config, content){
31857     config = config || {};
31858     config.fitToFrame = true;
31859     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31860     
31861     this.el.dom.style.overflow = "hidden";
31862     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31863     this.el.removeClass("x-layout-inactive-content");
31864     this.el.on("mousewheel", this.onWheel, this);
31865
31866     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31867     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31868     up.unselectable(); down.unselectable();
31869     up.on("click", this.scrollUp, this);
31870     down.on("click", this.scrollDown, this);
31871     up.addClassOnOver("x-scroller-btn-over");
31872     down.addClassOnOver("x-scroller-btn-over");
31873     up.addClassOnClick("x-scroller-btn-click");
31874     down.addClassOnClick("x-scroller-btn-click");
31875     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31876
31877     this.resizeEl = this.el;
31878     this.el = wrap; this.up = up; this.down = down;
31879 };
31880
31881 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31882     increment : 100,
31883     wheelIncrement : 5,
31884     scrollUp : function(){
31885         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31886     },
31887
31888     scrollDown : function(){
31889         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31890     },
31891
31892     afterScroll : function(){
31893         var el = this.resizeEl;
31894         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31895         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31896         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31897     },
31898
31899     setSize : function(){
31900         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31901         this.afterScroll();
31902     },
31903
31904     onWheel : function(e){
31905         var d = e.getWheelDelta();
31906         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31907         this.afterScroll();
31908         e.stopEvent();
31909     },
31910
31911     setContent : function(content, loadScripts){
31912         this.resizeEl.update(content, loadScripts);
31913     }
31914
31915 });
31916
31917
31918
31919 /**
31920  * @class Roo.TreePanel
31921  * @extends Roo.ContentPanel
31922  * Treepanel component
31923  * 
31924  * @constructor
31925  * Create a new TreePanel. - defaults to fit/scoll contents.
31926  * @param {String/Object} config A string to set only the panel's title, or a config object
31927  */
31928 Roo.TreePanel = function(config){
31929     var el = config.el;
31930     var tree = config.tree;
31931     delete config.tree; 
31932     delete config.el; // hopefull!
31933     
31934     // wrapper for IE7 strict & safari scroll issue
31935     
31936     var treeEl = el.createChild();
31937     config.resizeEl = treeEl;
31938     
31939     
31940     
31941     Roo.TreePanel.superclass.constructor.call(this, el, config);
31942  
31943  
31944     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31945     //console.log(tree);
31946     this.on('activate', function()
31947     {
31948         if (this.tree.rendered) {
31949             return;
31950         }
31951         //console.log('render tree');
31952         this.tree.render();
31953     });
31954     // this should not be needed.. - it's actually the 'el' that resizes?
31955     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31956     
31957     //this.on('resize',  function (cp, w, h) {
31958     //        this.tree.innerCt.setWidth(w);
31959     //        this.tree.innerCt.setHeight(h);
31960     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31961     //});
31962
31963         
31964     
31965 };
31966
31967 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31968     fitToFrame : true,
31969     autoScroll : true,
31970     /*
31971      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31972      */
31973     tree : false
31974
31975 });
31976
31977
31978
31979
31980
31981
31982
31983
31984
31985
31986
31987 /*
31988  * Based on:
31989  * Ext JS Library 1.1.1
31990  * Copyright(c) 2006-2007, Ext JS, LLC.
31991  *
31992  * Originally Released Under LGPL - original licence link has changed is not relivant.
31993  *
31994  * Fork - LGPL
31995  * <script type="text/javascript">
31996  */
31997  
31998
31999 /**
32000  * @class Roo.ReaderLayout
32001  * @extends Roo.BorderLayout
32002  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32003  * center region containing two nested regions (a top one for a list view and one for item preview below),
32004  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32005  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32006  * expedites the setup of the overall layout and regions for this common application style.
32007  * Example:
32008  <pre><code>
32009 var reader = new Roo.ReaderLayout();
32010 var CP = Roo.ContentPanel;  // shortcut for adding
32011
32012 reader.beginUpdate();
32013 reader.add("north", new CP("north", "North"));
32014 reader.add("west", new CP("west", {title: "West"}));
32015 reader.add("east", new CP("east", {title: "East"}));
32016
32017 reader.regions.listView.add(new CP("listView", "List"));
32018 reader.regions.preview.add(new CP("preview", "Preview"));
32019 reader.endUpdate();
32020 </code></pre>
32021 * @constructor
32022 * Create a new ReaderLayout
32023 * @param {Object} config Configuration options
32024 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32025 * document.body if omitted)
32026 */
32027 Roo.ReaderLayout = function(config, renderTo){
32028     var c = config || {size:{}};
32029     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32030         north: c.north !== false ? Roo.apply({
32031             split:false,
32032             initialSize: 32,
32033             titlebar: false
32034         }, c.north) : false,
32035         west: c.west !== false ? Roo.apply({
32036             split:true,
32037             initialSize: 200,
32038             minSize: 175,
32039             maxSize: 400,
32040             titlebar: true,
32041             collapsible: true,
32042             animate: true,
32043             margins:{left:5,right:0,bottom:5,top:5},
32044             cmargins:{left:5,right:5,bottom:5,top:5}
32045         }, c.west) : false,
32046         east: c.east !== false ? Roo.apply({
32047             split:true,
32048             initialSize: 200,
32049             minSize: 175,
32050             maxSize: 400,
32051             titlebar: true,
32052             collapsible: true,
32053             animate: true,
32054             margins:{left:0,right:5,bottom:5,top:5},
32055             cmargins:{left:5,right:5,bottom:5,top:5}
32056         }, c.east) : false,
32057         center: Roo.apply({
32058             tabPosition: 'top',
32059             autoScroll:false,
32060             closeOnTab: true,
32061             titlebar:false,
32062             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32063         }, c.center)
32064     });
32065
32066     this.el.addClass('x-reader');
32067
32068     this.beginUpdate();
32069
32070     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32071         south: c.preview !== false ? Roo.apply({
32072             split:true,
32073             initialSize: 200,
32074             minSize: 100,
32075             autoScroll:true,
32076             collapsible:true,
32077             titlebar: true,
32078             cmargins:{top:5,left:0, right:0, bottom:0}
32079         }, c.preview) : false,
32080         center: Roo.apply({
32081             autoScroll:false,
32082             titlebar:false,
32083             minHeight:200
32084         }, c.listView)
32085     });
32086     this.add('center', new Roo.NestedLayoutPanel(inner,
32087             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32088
32089     this.endUpdate();
32090
32091     this.regions.preview = inner.getRegion('south');
32092     this.regions.listView = inner.getRegion('center');
32093 };
32094
32095 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32096  * Based on:
32097  * Ext JS Library 1.1.1
32098  * Copyright(c) 2006-2007, Ext JS, LLC.
32099  *
32100  * Originally Released Under LGPL - original licence link has changed is not relivant.
32101  *
32102  * Fork - LGPL
32103  * <script type="text/javascript">
32104  */
32105  
32106 /**
32107  * @class Roo.grid.Grid
32108  * @extends Roo.util.Observable
32109  * This class represents the primary interface of a component based grid control.
32110  * <br><br>Usage:<pre><code>
32111  var grid = new Roo.grid.Grid("my-container-id", {
32112      ds: myDataStore,
32113      cm: myColModel,
32114      selModel: mySelectionModel,
32115      autoSizeColumns: true,
32116      monitorWindowResize: false,
32117      trackMouseOver: true
32118  });
32119  // set any options
32120  grid.render();
32121  * </code></pre>
32122  * <b>Common Problems:</b><br/>
32123  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32124  * element will correct this<br/>
32125  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32126  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32127  * are unpredictable.<br/>
32128  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32129  * grid to calculate dimensions/offsets.<br/>
32130   * @constructor
32131  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32132  * The container MUST have some type of size defined for the grid to fill. The container will be
32133  * automatically set to position relative if it isn't already.
32134  * @param {Object} config A config object that sets properties on this grid.
32135  */
32136 Roo.grid.Grid = function(container, config){
32137         // initialize the container
32138         this.container = Roo.get(container);
32139         this.container.update("");
32140         this.container.setStyle("overflow", "hidden");
32141     this.container.addClass('x-grid-container');
32142
32143     this.id = this.container.id;
32144
32145     Roo.apply(this, config);
32146     // check and correct shorthanded configs
32147     if(this.ds){
32148         this.dataSource = this.ds;
32149         delete this.ds;
32150     }
32151     if(this.cm){
32152         this.colModel = this.cm;
32153         delete this.cm;
32154     }
32155     if(this.sm){
32156         this.selModel = this.sm;
32157         delete this.sm;
32158     }
32159
32160     if (this.selModel) {
32161         this.selModel = Roo.factory(this.selModel, Roo.grid);
32162         this.sm = this.selModel;
32163         this.sm.xmodule = this.xmodule || false;
32164     }
32165     if (typeof(this.colModel.config) == 'undefined') {
32166         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32167         this.cm = this.colModel;
32168         this.cm.xmodule = this.xmodule || false;
32169     }
32170     if (this.dataSource) {
32171         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32172         this.ds = this.dataSource;
32173         this.ds.xmodule = this.xmodule || false;
32174          
32175     }
32176     
32177     
32178     
32179     if(this.width){
32180         this.container.setWidth(this.width);
32181     }
32182
32183     if(this.height){
32184         this.container.setHeight(this.height);
32185     }
32186     /** @private */
32187         this.addEvents({
32188         // raw events
32189         /**
32190          * @event click
32191          * The raw click event for the entire grid.
32192          * @param {Roo.EventObject} e
32193          */
32194         "click" : true,
32195         /**
32196          * @event dblclick
32197          * The raw dblclick event for the entire grid.
32198          * @param {Roo.EventObject} e
32199          */
32200         "dblclick" : true,
32201         /**
32202          * @event contextmenu
32203          * The raw contextmenu event for the entire grid.
32204          * @param {Roo.EventObject} e
32205          */
32206         "contextmenu" : true,
32207         /**
32208          * @event mousedown
32209          * The raw mousedown event for the entire grid.
32210          * @param {Roo.EventObject} e
32211          */
32212         "mousedown" : true,
32213         /**
32214          * @event mouseup
32215          * The raw mouseup event for the entire grid.
32216          * @param {Roo.EventObject} e
32217          */
32218         "mouseup" : true,
32219         /**
32220          * @event mouseover
32221          * The raw mouseover event for the entire grid.
32222          * @param {Roo.EventObject} e
32223          */
32224         "mouseover" : true,
32225         /**
32226          * @event mouseout
32227          * The raw mouseout event for the entire grid.
32228          * @param {Roo.EventObject} e
32229          */
32230         "mouseout" : true,
32231         /**
32232          * @event keypress
32233          * The raw keypress event for the entire grid.
32234          * @param {Roo.EventObject} e
32235          */
32236         "keypress" : true,
32237         /**
32238          * @event keydown
32239          * The raw keydown event for the entire grid.
32240          * @param {Roo.EventObject} e
32241          */
32242         "keydown" : true,
32243
32244         // custom events
32245
32246         /**
32247          * @event cellclick
32248          * Fires when a cell is clicked
32249          * @param {Grid} this
32250          * @param {Number} rowIndex
32251          * @param {Number} columnIndex
32252          * @param {Roo.EventObject} e
32253          */
32254         "cellclick" : true,
32255         /**
32256          * @event celldblclick
32257          * Fires when a cell is double clicked
32258          * @param {Grid} this
32259          * @param {Number} rowIndex
32260          * @param {Number} columnIndex
32261          * @param {Roo.EventObject} e
32262          */
32263         "celldblclick" : true,
32264         /**
32265          * @event rowclick
32266          * Fires when a row is clicked
32267          * @param {Grid} this
32268          * @param {Number} rowIndex
32269          * @param {Roo.EventObject} e
32270          */
32271         "rowclick" : true,
32272         /**
32273          * @event rowdblclick
32274          * Fires when a row is double clicked
32275          * @param {Grid} this
32276          * @param {Number} rowIndex
32277          * @param {Roo.EventObject} e
32278          */
32279         "rowdblclick" : true,
32280         /**
32281          * @event headerclick
32282          * Fires when a header is clicked
32283          * @param {Grid} this
32284          * @param {Number} columnIndex
32285          * @param {Roo.EventObject} e
32286          */
32287         "headerclick" : true,
32288         /**
32289          * @event headerdblclick
32290          * Fires when a header cell is double clicked
32291          * @param {Grid} this
32292          * @param {Number} columnIndex
32293          * @param {Roo.EventObject} e
32294          */
32295         "headerdblclick" : true,
32296         /**
32297          * @event rowcontextmenu
32298          * Fires when a row is right clicked
32299          * @param {Grid} this
32300          * @param {Number} rowIndex
32301          * @param {Roo.EventObject} e
32302          */
32303         "rowcontextmenu" : true,
32304         /**
32305          * @event cellcontextmenu
32306          * Fires when a cell is right clicked
32307          * @param {Grid} this
32308          * @param {Number} rowIndex
32309          * @param {Number} cellIndex
32310          * @param {Roo.EventObject} e
32311          */
32312          "cellcontextmenu" : true,
32313         /**
32314          * @event headercontextmenu
32315          * Fires when a header is right clicked
32316          * @param {Grid} this
32317          * @param {Number} columnIndex
32318          * @param {Roo.EventObject} e
32319          */
32320         "headercontextmenu" : true,
32321         /**
32322          * @event bodyscroll
32323          * Fires when the body element is scrolled
32324          * @param {Number} scrollLeft
32325          * @param {Number} scrollTop
32326          */
32327         "bodyscroll" : true,
32328         /**
32329          * @event columnresize
32330          * Fires when the user resizes a column
32331          * @param {Number} columnIndex
32332          * @param {Number} newSize
32333          */
32334         "columnresize" : true,
32335         /**
32336          * @event columnmove
32337          * Fires when the user moves a column
32338          * @param {Number} oldIndex
32339          * @param {Number} newIndex
32340          */
32341         "columnmove" : true,
32342         /**
32343          * @event startdrag
32344          * Fires when row(s) start being dragged
32345          * @param {Grid} this
32346          * @param {Roo.GridDD} dd The drag drop object
32347          * @param {event} e The raw browser event
32348          */
32349         "startdrag" : true,
32350         /**
32351          * @event enddrag
32352          * Fires when a drag operation is complete
32353          * @param {Grid} this
32354          * @param {Roo.GridDD} dd The drag drop object
32355          * @param {event} e The raw browser event
32356          */
32357         "enddrag" : true,
32358         /**
32359          * @event dragdrop
32360          * Fires when dragged row(s) are dropped on a valid DD target
32361          * @param {Grid} this
32362          * @param {Roo.GridDD} dd The drag drop object
32363          * @param {String} targetId The target drag drop object
32364          * @param {event} e The raw browser event
32365          */
32366         "dragdrop" : true,
32367         /**
32368          * @event dragover
32369          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32370          * @param {Grid} this
32371          * @param {Roo.GridDD} dd The drag drop object
32372          * @param {String} targetId The target drag drop object
32373          * @param {event} e The raw browser event
32374          */
32375         "dragover" : true,
32376         /**
32377          * @event dragenter
32378          *  Fires when the dragged row(s) first cross another DD target while being dragged
32379          * @param {Grid} this
32380          * @param {Roo.GridDD} dd The drag drop object
32381          * @param {String} targetId The target drag drop object
32382          * @param {event} e The raw browser event
32383          */
32384         "dragenter" : true,
32385         /**
32386          * @event dragout
32387          * Fires when the dragged row(s) leave another DD target while being dragged
32388          * @param {Grid} this
32389          * @param {Roo.GridDD} dd The drag drop object
32390          * @param {String} targetId The target drag drop object
32391          * @param {event} e The raw browser event
32392          */
32393         "dragout" : true,
32394         /**
32395          * @event rowclass
32396          * Fires when a row is rendered, so you can change add a style to it.
32397          * @param {GridView} gridview   The grid view
32398          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32399          */
32400         'rowclass' : true,
32401
32402         /**
32403          * @event render
32404          * Fires when the grid is rendered
32405          * @param {Grid} grid
32406          */
32407         'render' : true
32408     });
32409
32410     Roo.grid.Grid.superclass.constructor.call(this);
32411 };
32412 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32413     
32414     /**
32415      * @cfg {String} ddGroup - drag drop group.
32416      */
32417       /**
32418      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32419      */
32420
32421     /**
32422      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32423      */
32424     minColumnWidth : 25,
32425
32426     /**
32427      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32428      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32429      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32430      */
32431     autoSizeColumns : false,
32432
32433     /**
32434      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32435      */
32436     autoSizeHeaders : true,
32437
32438     /**
32439      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32440      */
32441     monitorWindowResize : true,
32442
32443     /**
32444      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32445      * rows measured to get a columns size. Default is 0 (all rows).
32446      */
32447     maxRowsToMeasure : 0,
32448
32449     /**
32450      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32451      */
32452     trackMouseOver : true,
32453
32454     /**
32455     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32456     */
32457       /**
32458     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32459     */
32460     
32461     /**
32462     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32463     */
32464     enableDragDrop : false,
32465     
32466     /**
32467     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32468     */
32469     enableColumnMove : true,
32470     
32471     /**
32472     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32473     */
32474     enableColumnHide : true,
32475     
32476     /**
32477     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32478     */
32479     enableRowHeightSync : false,
32480     
32481     /**
32482     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32483     */
32484     stripeRows : true,
32485     
32486     /**
32487     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32488     */
32489     autoHeight : false,
32490
32491     /**
32492      * @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.
32493      */
32494     autoExpandColumn : false,
32495
32496     /**
32497     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32498     * Default is 50.
32499     */
32500     autoExpandMin : 50,
32501
32502     /**
32503     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32504     */
32505     autoExpandMax : 1000,
32506
32507     /**
32508     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32509     */
32510     view : null,
32511
32512     /**
32513     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32514     */
32515     loadMask : false,
32516     /**
32517     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32518     */
32519     dropTarget: false,
32520     
32521    
32522     
32523     // private
32524     rendered : false,
32525
32526     /**
32527     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32528     * of a fixed width. Default is false.
32529     */
32530     /**
32531     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32532     */
32533     
32534     
32535     /**
32536     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32537     * %0 is replaced with the number of selected rows.
32538     */
32539     ddText : "{0} selected row{1}",
32540     
32541     
32542     /**
32543      * Called once after all setup has been completed and the grid is ready to be rendered.
32544      * @return {Roo.grid.Grid} this
32545      */
32546     render : function()
32547     {
32548         var c = this.container;
32549         // try to detect autoHeight/width mode
32550         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32551             this.autoHeight = true;
32552         }
32553         var view = this.getView();
32554         view.init(this);
32555
32556         c.on("click", this.onClick, this);
32557         c.on("dblclick", this.onDblClick, this);
32558         c.on("contextmenu", this.onContextMenu, this);
32559         c.on("keydown", this.onKeyDown, this);
32560         if (Roo.isTouch) {
32561             c.on("touchstart", this.onTouchStart, this);
32562         }
32563
32564         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32565
32566         this.getSelectionModel().init(this);
32567
32568         view.render();
32569
32570         if(this.loadMask){
32571             this.loadMask = new Roo.LoadMask(this.container,
32572                     Roo.apply({store:this.dataSource}, this.loadMask));
32573         }
32574         
32575         
32576         if (this.toolbar && this.toolbar.xtype) {
32577             this.toolbar.container = this.getView().getHeaderPanel(true);
32578             this.toolbar = new Roo.Toolbar(this.toolbar);
32579         }
32580         if (this.footer && this.footer.xtype) {
32581             this.footer.dataSource = this.getDataSource();
32582             this.footer.container = this.getView().getFooterPanel(true);
32583             this.footer = Roo.factory(this.footer, Roo);
32584         }
32585         if (this.dropTarget && this.dropTarget.xtype) {
32586             delete this.dropTarget.xtype;
32587             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32588         }
32589         
32590         
32591         this.rendered = true;
32592         this.fireEvent('render', this);
32593         return this;
32594     },
32595
32596     /**
32597      * Reconfigures the grid to use a different Store and Column Model.
32598      * The View will be bound to the new objects and refreshed.
32599      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32600      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32601      */
32602     reconfigure : function(dataSource, colModel){
32603         if(this.loadMask){
32604             this.loadMask.destroy();
32605             this.loadMask = new Roo.LoadMask(this.container,
32606                     Roo.apply({store:dataSource}, this.loadMask));
32607         }
32608         this.view.bind(dataSource, colModel);
32609         this.dataSource = dataSource;
32610         this.colModel = colModel;
32611         this.view.refresh(true);
32612     },
32613     /**
32614      * addColumns
32615      * Add's a column, default at the end..
32616      
32617      * @param {int} position to add (default end)
32618      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32619      */
32620     addColumns : function(pos, ar)
32621     {
32622         
32623         for (var i =0;i< ar.length;i++) {
32624             var cfg = ar[i];
32625             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32626             this.cm.lookup[cfg.id] = cfg;
32627         }
32628         
32629         
32630         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32631             pos = this.cm.config.length; //this.cm.config.push(cfg);
32632         } 
32633         pos = Math.max(0,pos);
32634         ar.unshift(0);
32635         ar.unshift(pos);
32636         this.cm.config.splice.apply(this.cm.config, ar);
32637         
32638         
32639         
32640         this.view.generateRules(this.cm);
32641         this.view.refresh(true);
32642         
32643     },
32644     
32645     
32646     
32647     
32648     // private
32649     onKeyDown : function(e){
32650         this.fireEvent("keydown", e);
32651     },
32652
32653     /**
32654      * Destroy this grid.
32655      * @param {Boolean} removeEl True to remove the element
32656      */
32657     destroy : function(removeEl, keepListeners){
32658         if(this.loadMask){
32659             this.loadMask.destroy();
32660         }
32661         var c = this.container;
32662         c.removeAllListeners();
32663         this.view.destroy();
32664         this.colModel.purgeListeners();
32665         if(!keepListeners){
32666             this.purgeListeners();
32667         }
32668         c.update("");
32669         if(removeEl === true){
32670             c.remove();
32671         }
32672     },
32673
32674     // private
32675     processEvent : function(name, e){
32676         // does this fire select???
32677         //Roo.log('grid:processEvent '  + name);
32678         
32679         if (name != 'touchstart' ) {
32680             this.fireEvent(name, e);    
32681         }
32682         
32683         var t = e.getTarget();
32684         var v = this.view;
32685         var header = v.findHeaderIndex(t);
32686         if(header !== false){
32687             var ename = name == 'touchstart' ? 'click' : name;
32688              
32689             this.fireEvent("header" + ename, this, header, e);
32690         }else{
32691             var row = v.findRowIndex(t);
32692             var cell = v.findCellIndex(t);
32693             if (name == 'touchstart') {
32694                 // first touch is always a click.
32695                 // hopefull this happens after selection is updated.?
32696                 name = false;
32697                 
32698                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32699                     var cs = this.selModel.getSelectedCell();
32700                     if (row == cs[0] && cell == cs[1]){
32701                         name = 'dblclick';
32702                     }
32703                 }
32704                 if (typeof(this.selModel.getSelections) != 'undefined') {
32705                     var cs = this.selModel.getSelections();
32706                     var ds = this.dataSource;
32707                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32708                         name = 'dblclick';
32709                     }
32710                 }
32711                 if (!name) {
32712                     return;
32713                 }
32714             }
32715             
32716             
32717             if(row !== false){
32718                 this.fireEvent("row" + name, this, row, e);
32719                 if(cell !== false){
32720                     this.fireEvent("cell" + name, this, row, cell, e);
32721                 }
32722             }
32723         }
32724     },
32725
32726     // private
32727     onClick : function(e){
32728         this.processEvent("click", e);
32729     },
32730    // private
32731     onTouchStart : function(e){
32732         this.processEvent("touchstart", e);
32733     },
32734
32735     // private
32736     onContextMenu : function(e, t){
32737         this.processEvent("contextmenu", e);
32738     },
32739
32740     // private
32741     onDblClick : function(e){
32742         this.processEvent("dblclick", e);
32743     },
32744
32745     // private
32746     walkCells : function(row, col, step, fn, scope){
32747         var cm = this.colModel, clen = cm.getColumnCount();
32748         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32749         if(step < 0){
32750             if(col < 0){
32751                 row--;
32752                 first = false;
32753             }
32754             while(row >= 0){
32755                 if(!first){
32756                     col = clen-1;
32757                 }
32758                 first = false;
32759                 while(col >= 0){
32760                     if(fn.call(scope || this, row, col, cm) === true){
32761                         return [row, col];
32762                     }
32763                     col--;
32764                 }
32765                 row--;
32766             }
32767         } else {
32768             if(col >= clen){
32769                 row++;
32770                 first = false;
32771             }
32772             while(row < rlen){
32773                 if(!first){
32774                     col = 0;
32775                 }
32776                 first = false;
32777                 while(col < clen){
32778                     if(fn.call(scope || this, row, col, cm) === true){
32779                         return [row, col];
32780                     }
32781                     col++;
32782                 }
32783                 row++;
32784             }
32785         }
32786         return null;
32787     },
32788
32789     // private
32790     getSelections : function(){
32791         return this.selModel.getSelections();
32792     },
32793
32794     /**
32795      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32796      * but if manual update is required this method will initiate it.
32797      */
32798     autoSize : function(){
32799         if(this.rendered){
32800             this.view.layout();
32801             if(this.view.adjustForScroll){
32802                 this.view.adjustForScroll();
32803             }
32804         }
32805     },
32806
32807     /**
32808      * Returns the grid's underlying element.
32809      * @return {Element} The element
32810      */
32811     getGridEl : function(){
32812         return this.container;
32813     },
32814
32815     // private for compatibility, overridden by editor grid
32816     stopEditing : function(){},
32817
32818     /**
32819      * Returns the grid's SelectionModel.
32820      * @return {SelectionModel}
32821      */
32822     getSelectionModel : function(){
32823         if(!this.selModel){
32824             this.selModel = new Roo.grid.RowSelectionModel();
32825         }
32826         return this.selModel;
32827     },
32828
32829     /**
32830      * Returns the grid's DataSource.
32831      * @return {DataSource}
32832      */
32833     getDataSource : function(){
32834         return this.dataSource;
32835     },
32836
32837     /**
32838      * Returns the grid's ColumnModel.
32839      * @return {ColumnModel}
32840      */
32841     getColumnModel : function(){
32842         return this.colModel;
32843     },
32844
32845     /**
32846      * Returns the grid's GridView object.
32847      * @return {GridView}
32848      */
32849     getView : function(){
32850         if(!this.view){
32851             this.view = new Roo.grid.GridView(this.viewConfig);
32852             this.relayEvents(this.view, [
32853                 "beforerowremoved", "beforerowsinserted",
32854                 "beforerefresh", "rowremoved",
32855                 "rowsinserted", "rowupdated" ,"refresh"
32856             ]);
32857         }
32858         return this.view;
32859     },
32860     /**
32861      * Called to get grid's drag proxy text, by default returns this.ddText.
32862      * Override this to put something different in the dragged text.
32863      * @return {String}
32864      */
32865     getDragDropText : function(){
32866         var count = this.selModel.getCount();
32867         return String.format(this.ddText, count, count == 1 ? '' : 's');
32868     }
32869 });
32870 /*
32871  * Based on:
32872  * Ext JS Library 1.1.1
32873  * Copyright(c) 2006-2007, Ext JS, LLC.
32874  *
32875  * Originally Released Under LGPL - original licence link has changed is not relivant.
32876  *
32877  * Fork - LGPL
32878  * <script type="text/javascript">
32879  */
32880  
32881 Roo.grid.AbstractGridView = function(){
32882         this.grid = null;
32883         
32884         this.events = {
32885             "beforerowremoved" : true,
32886             "beforerowsinserted" : true,
32887             "beforerefresh" : true,
32888             "rowremoved" : true,
32889             "rowsinserted" : true,
32890             "rowupdated" : true,
32891             "refresh" : true
32892         };
32893     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32894 };
32895
32896 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32897     rowClass : "x-grid-row",
32898     cellClass : "x-grid-cell",
32899     tdClass : "x-grid-td",
32900     hdClass : "x-grid-hd",
32901     splitClass : "x-grid-hd-split",
32902     
32903     init: function(grid){
32904         this.grid = grid;
32905                 var cid = this.grid.getGridEl().id;
32906         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32907         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32908         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32909         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32910         },
32911         
32912     getColumnRenderers : function(){
32913         var renderers = [];
32914         var cm = this.grid.colModel;
32915         var colCount = cm.getColumnCount();
32916         for(var i = 0; i < colCount; i++){
32917             renderers[i] = cm.getRenderer(i);
32918         }
32919         return renderers;
32920     },
32921     
32922     getColumnIds : function(){
32923         var ids = [];
32924         var cm = this.grid.colModel;
32925         var colCount = cm.getColumnCount();
32926         for(var i = 0; i < colCount; i++){
32927             ids[i] = cm.getColumnId(i);
32928         }
32929         return ids;
32930     },
32931     
32932     getDataIndexes : function(){
32933         if(!this.indexMap){
32934             this.indexMap = this.buildIndexMap();
32935         }
32936         return this.indexMap.colToData;
32937     },
32938     
32939     getColumnIndexByDataIndex : function(dataIndex){
32940         if(!this.indexMap){
32941             this.indexMap = this.buildIndexMap();
32942         }
32943         return this.indexMap.dataToCol[dataIndex];
32944     },
32945     
32946     /**
32947      * Set a css style for a column dynamically. 
32948      * @param {Number} colIndex The index of the column
32949      * @param {String} name The css property name
32950      * @param {String} value The css value
32951      */
32952     setCSSStyle : function(colIndex, name, value){
32953         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32954         Roo.util.CSS.updateRule(selector, name, value);
32955     },
32956     
32957     generateRules : function(cm){
32958         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32959         Roo.util.CSS.removeStyleSheet(rulesId);
32960         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32961             var cid = cm.getColumnId(i);
32962             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32963                          this.tdSelector, cid, " {\n}\n",
32964                          this.hdSelector, cid, " {\n}\n",
32965                          this.splitSelector, cid, " {\n}\n");
32966         }
32967         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32968     }
32969 });/*
32970  * Based on:
32971  * Ext JS Library 1.1.1
32972  * Copyright(c) 2006-2007, Ext JS, LLC.
32973  *
32974  * Originally Released Under LGPL - original licence link has changed is not relivant.
32975  *
32976  * Fork - LGPL
32977  * <script type="text/javascript">
32978  */
32979
32980 // private
32981 // This is a support class used internally by the Grid components
32982 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32983     this.grid = grid;
32984     this.view = grid.getView();
32985     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32986     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32987     if(hd2){
32988         this.setHandleElId(Roo.id(hd));
32989         this.setOuterHandleElId(Roo.id(hd2));
32990     }
32991     this.scroll = false;
32992 };
32993 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32994     maxDragWidth: 120,
32995     getDragData : function(e){
32996         var t = Roo.lib.Event.getTarget(e);
32997         var h = this.view.findHeaderCell(t);
32998         if(h){
32999             return {ddel: h.firstChild, header:h};
33000         }
33001         return false;
33002     },
33003
33004     onInitDrag : function(e){
33005         this.view.headersDisabled = true;
33006         var clone = this.dragData.ddel.cloneNode(true);
33007         clone.id = Roo.id();
33008         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33009         this.proxy.update(clone);
33010         return true;
33011     },
33012
33013     afterValidDrop : function(){
33014         var v = this.view;
33015         setTimeout(function(){
33016             v.headersDisabled = false;
33017         }, 50);
33018     },
33019
33020     afterInvalidDrop : function(){
33021         var v = this.view;
33022         setTimeout(function(){
33023             v.headersDisabled = false;
33024         }, 50);
33025     }
33026 });
33027 /*
33028  * Based on:
33029  * Ext JS Library 1.1.1
33030  * Copyright(c) 2006-2007, Ext JS, LLC.
33031  *
33032  * Originally Released Under LGPL - original licence link has changed is not relivant.
33033  *
33034  * Fork - LGPL
33035  * <script type="text/javascript">
33036  */
33037 // private
33038 // This is a support class used internally by the Grid components
33039 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33040     this.grid = grid;
33041     this.view = grid.getView();
33042     // split the proxies so they don't interfere with mouse events
33043     this.proxyTop = Roo.DomHelper.append(document.body, {
33044         cls:"col-move-top", html:"&#160;"
33045     }, true);
33046     this.proxyBottom = Roo.DomHelper.append(document.body, {
33047         cls:"col-move-bottom", html:"&#160;"
33048     }, true);
33049     this.proxyTop.hide = this.proxyBottom.hide = function(){
33050         this.setLeftTop(-100,-100);
33051         this.setStyle("visibility", "hidden");
33052     };
33053     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33054     // temporarily disabled
33055     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33056     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33057 };
33058 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33059     proxyOffsets : [-4, -9],
33060     fly: Roo.Element.fly,
33061
33062     getTargetFromEvent : function(e){
33063         var t = Roo.lib.Event.getTarget(e);
33064         var cindex = this.view.findCellIndex(t);
33065         if(cindex !== false){
33066             return this.view.getHeaderCell(cindex);
33067         }
33068         return null;
33069     },
33070
33071     nextVisible : function(h){
33072         var v = this.view, cm = this.grid.colModel;
33073         h = h.nextSibling;
33074         while(h){
33075             if(!cm.isHidden(v.getCellIndex(h))){
33076                 return h;
33077             }
33078             h = h.nextSibling;
33079         }
33080         return null;
33081     },
33082
33083     prevVisible : function(h){
33084         var v = this.view, cm = this.grid.colModel;
33085         h = h.prevSibling;
33086         while(h){
33087             if(!cm.isHidden(v.getCellIndex(h))){
33088                 return h;
33089             }
33090             h = h.prevSibling;
33091         }
33092         return null;
33093     },
33094
33095     positionIndicator : function(h, n, e){
33096         var x = Roo.lib.Event.getPageX(e);
33097         var r = Roo.lib.Dom.getRegion(n.firstChild);
33098         var px, pt, py = r.top + this.proxyOffsets[1];
33099         if((r.right - x) <= (r.right-r.left)/2){
33100             px = r.right+this.view.borderWidth;
33101             pt = "after";
33102         }else{
33103             px = r.left;
33104             pt = "before";
33105         }
33106         var oldIndex = this.view.getCellIndex(h);
33107         var newIndex = this.view.getCellIndex(n);
33108
33109         if(this.grid.colModel.isFixed(newIndex)){
33110             return false;
33111         }
33112
33113         var locked = this.grid.colModel.isLocked(newIndex);
33114
33115         if(pt == "after"){
33116             newIndex++;
33117         }
33118         if(oldIndex < newIndex){
33119             newIndex--;
33120         }
33121         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33122             return false;
33123         }
33124         px +=  this.proxyOffsets[0];
33125         this.proxyTop.setLeftTop(px, py);
33126         this.proxyTop.show();
33127         if(!this.bottomOffset){
33128             this.bottomOffset = this.view.mainHd.getHeight();
33129         }
33130         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33131         this.proxyBottom.show();
33132         return pt;
33133     },
33134
33135     onNodeEnter : function(n, dd, e, data){
33136         if(data.header != n){
33137             this.positionIndicator(data.header, n, e);
33138         }
33139     },
33140
33141     onNodeOver : function(n, dd, e, data){
33142         var result = false;
33143         if(data.header != n){
33144             result = this.positionIndicator(data.header, n, e);
33145         }
33146         if(!result){
33147             this.proxyTop.hide();
33148             this.proxyBottom.hide();
33149         }
33150         return result ? this.dropAllowed : this.dropNotAllowed;
33151     },
33152
33153     onNodeOut : function(n, dd, e, data){
33154         this.proxyTop.hide();
33155         this.proxyBottom.hide();
33156     },
33157
33158     onNodeDrop : function(n, dd, e, data){
33159         var h = data.header;
33160         if(h != n){
33161             var cm = this.grid.colModel;
33162             var x = Roo.lib.Event.getPageX(e);
33163             var r = Roo.lib.Dom.getRegion(n.firstChild);
33164             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33165             var oldIndex = this.view.getCellIndex(h);
33166             var newIndex = this.view.getCellIndex(n);
33167             var locked = cm.isLocked(newIndex);
33168             if(pt == "after"){
33169                 newIndex++;
33170             }
33171             if(oldIndex < newIndex){
33172                 newIndex--;
33173             }
33174             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33175                 return false;
33176             }
33177             cm.setLocked(oldIndex, locked, true);
33178             cm.moveColumn(oldIndex, newIndex);
33179             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33180             return true;
33181         }
33182         return false;
33183     }
33184 });
33185 /*
33186  * Based on:
33187  * Ext JS Library 1.1.1
33188  * Copyright(c) 2006-2007, Ext JS, LLC.
33189  *
33190  * Originally Released Under LGPL - original licence link has changed is not relivant.
33191  *
33192  * Fork - LGPL
33193  * <script type="text/javascript">
33194  */
33195   
33196 /**
33197  * @class Roo.grid.GridView
33198  * @extends Roo.util.Observable
33199  *
33200  * @constructor
33201  * @param {Object} config
33202  */
33203 Roo.grid.GridView = function(config){
33204     Roo.grid.GridView.superclass.constructor.call(this);
33205     this.el = null;
33206
33207     Roo.apply(this, config);
33208 };
33209
33210 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33211
33212     unselectable :  'unselectable="on"',
33213     unselectableCls :  'x-unselectable',
33214     
33215     
33216     rowClass : "x-grid-row",
33217
33218     cellClass : "x-grid-col",
33219
33220     tdClass : "x-grid-td",
33221
33222     hdClass : "x-grid-hd",
33223
33224     splitClass : "x-grid-split",
33225
33226     sortClasses : ["sort-asc", "sort-desc"],
33227
33228     enableMoveAnim : false,
33229
33230     hlColor: "C3DAF9",
33231
33232     dh : Roo.DomHelper,
33233
33234     fly : Roo.Element.fly,
33235
33236     css : Roo.util.CSS,
33237
33238     borderWidth: 1,
33239
33240     splitOffset: 3,
33241
33242     scrollIncrement : 22,
33243
33244     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33245
33246     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33247
33248     bind : function(ds, cm){
33249         if(this.ds){
33250             this.ds.un("load", this.onLoad, this);
33251             this.ds.un("datachanged", this.onDataChange, this);
33252             this.ds.un("add", this.onAdd, this);
33253             this.ds.un("remove", this.onRemove, this);
33254             this.ds.un("update", this.onUpdate, this);
33255             this.ds.un("clear", this.onClear, this);
33256         }
33257         if(ds){
33258             ds.on("load", this.onLoad, this);
33259             ds.on("datachanged", this.onDataChange, this);
33260             ds.on("add", this.onAdd, this);
33261             ds.on("remove", this.onRemove, this);
33262             ds.on("update", this.onUpdate, this);
33263             ds.on("clear", this.onClear, this);
33264         }
33265         this.ds = ds;
33266
33267         if(this.cm){
33268             this.cm.un("widthchange", this.onColWidthChange, this);
33269             this.cm.un("headerchange", this.onHeaderChange, this);
33270             this.cm.un("hiddenchange", this.onHiddenChange, this);
33271             this.cm.un("columnmoved", this.onColumnMove, this);
33272             this.cm.un("columnlockchange", this.onColumnLock, this);
33273         }
33274         if(cm){
33275             this.generateRules(cm);
33276             cm.on("widthchange", this.onColWidthChange, this);
33277             cm.on("headerchange", this.onHeaderChange, this);
33278             cm.on("hiddenchange", this.onHiddenChange, this);
33279             cm.on("columnmoved", this.onColumnMove, this);
33280             cm.on("columnlockchange", this.onColumnLock, this);
33281         }
33282         this.cm = cm;
33283     },
33284
33285     init: function(grid){
33286         Roo.grid.GridView.superclass.init.call(this, grid);
33287
33288         this.bind(grid.dataSource, grid.colModel);
33289
33290         grid.on("headerclick", this.handleHeaderClick, this);
33291
33292         if(grid.trackMouseOver){
33293             grid.on("mouseover", this.onRowOver, this);
33294             grid.on("mouseout", this.onRowOut, this);
33295         }
33296         grid.cancelTextSelection = function(){};
33297         this.gridId = grid.id;
33298
33299         var tpls = this.templates || {};
33300
33301         if(!tpls.master){
33302             tpls.master = new Roo.Template(
33303                '<div class="x-grid" hidefocus="true">',
33304                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33305                   '<div class="x-grid-topbar"></div>',
33306                   '<div class="x-grid-scroller"><div></div></div>',
33307                   '<div class="x-grid-locked">',
33308                       '<div class="x-grid-header">{lockedHeader}</div>',
33309                       '<div class="x-grid-body">{lockedBody}</div>',
33310                   "</div>",
33311                   '<div class="x-grid-viewport">',
33312                       '<div class="x-grid-header">{header}</div>',
33313                       '<div class="x-grid-body">{body}</div>',
33314                   "</div>",
33315                   '<div class="x-grid-bottombar"></div>',
33316                  
33317                   '<div class="x-grid-resize-proxy">&#160;</div>',
33318                "</div>"
33319             );
33320             tpls.master.disableformats = true;
33321         }
33322
33323         if(!tpls.header){
33324             tpls.header = new Roo.Template(
33325                '<table border="0" cellspacing="0" cellpadding="0">',
33326                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33327                "</table>{splits}"
33328             );
33329             tpls.header.disableformats = true;
33330         }
33331         tpls.header.compile();
33332
33333         if(!tpls.hcell){
33334             tpls.hcell = new Roo.Template(
33335                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33336                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33337                 "</div></td>"
33338              );
33339              tpls.hcell.disableFormats = true;
33340         }
33341         tpls.hcell.compile();
33342
33343         if(!tpls.hsplit){
33344             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33345                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33346             tpls.hsplit.disableFormats = true;
33347         }
33348         tpls.hsplit.compile();
33349
33350         if(!tpls.body){
33351             tpls.body = new Roo.Template(
33352                '<table border="0" cellspacing="0" cellpadding="0">',
33353                "<tbody>{rows}</tbody>",
33354                "</table>"
33355             );
33356             tpls.body.disableFormats = true;
33357         }
33358         tpls.body.compile();
33359
33360         if(!tpls.row){
33361             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33362             tpls.row.disableFormats = true;
33363         }
33364         tpls.row.compile();
33365
33366         if(!tpls.cell){
33367             tpls.cell = new Roo.Template(
33368                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33369                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33370                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33371                 "</td>"
33372             );
33373             tpls.cell.disableFormats = true;
33374         }
33375         tpls.cell.compile();
33376
33377         this.templates = tpls;
33378     },
33379
33380     // remap these for backwards compat
33381     onColWidthChange : function(){
33382         this.updateColumns.apply(this, arguments);
33383     },
33384     onHeaderChange : function(){
33385         this.updateHeaders.apply(this, arguments);
33386     }, 
33387     onHiddenChange : function(){
33388         this.handleHiddenChange.apply(this, arguments);
33389     },
33390     onColumnMove : function(){
33391         this.handleColumnMove.apply(this, arguments);
33392     },
33393     onColumnLock : function(){
33394         this.handleLockChange.apply(this, arguments);
33395     },
33396
33397     onDataChange : function(){
33398         this.refresh();
33399         this.updateHeaderSortState();
33400     },
33401
33402     onClear : function(){
33403         this.refresh();
33404     },
33405
33406     onUpdate : function(ds, record){
33407         this.refreshRow(record);
33408     },
33409
33410     refreshRow : function(record){
33411         var ds = this.ds, index;
33412         if(typeof record == 'number'){
33413             index = record;
33414             record = ds.getAt(index);
33415         }else{
33416             index = ds.indexOf(record);
33417         }
33418         this.insertRows(ds, index, index, true);
33419         this.onRemove(ds, record, index+1, true);
33420         this.syncRowHeights(index, index);
33421         this.layout();
33422         this.fireEvent("rowupdated", this, index, record);
33423     },
33424
33425     onAdd : function(ds, records, index){
33426         this.insertRows(ds, index, index + (records.length-1));
33427     },
33428
33429     onRemove : function(ds, record, index, isUpdate){
33430         if(isUpdate !== true){
33431             this.fireEvent("beforerowremoved", this, index, record);
33432         }
33433         var bt = this.getBodyTable(), lt = this.getLockedTable();
33434         if(bt.rows[index]){
33435             bt.firstChild.removeChild(bt.rows[index]);
33436         }
33437         if(lt.rows[index]){
33438             lt.firstChild.removeChild(lt.rows[index]);
33439         }
33440         if(isUpdate !== true){
33441             this.stripeRows(index);
33442             this.syncRowHeights(index, index);
33443             this.layout();
33444             this.fireEvent("rowremoved", this, index, record);
33445         }
33446     },
33447
33448     onLoad : function(){
33449         this.scrollToTop();
33450     },
33451
33452     /**
33453      * Scrolls the grid to the top
33454      */
33455     scrollToTop : function(){
33456         if(this.scroller){
33457             this.scroller.dom.scrollTop = 0;
33458             this.syncScroll();
33459         }
33460     },
33461
33462     /**
33463      * Gets a panel in the header of the grid that can be used for toolbars etc.
33464      * After modifying the contents of this panel a call to grid.autoSize() may be
33465      * required to register any changes in size.
33466      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33467      * @return Roo.Element
33468      */
33469     getHeaderPanel : function(doShow){
33470         if(doShow){
33471             this.headerPanel.show();
33472         }
33473         return this.headerPanel;
33474     },
33475
33476     /**
33477      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33478      * After modifying the contents of this panel a call to grid.autoSize() may be
33479      * required to register any changes in size.
33480      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33481      * @return Roo.Element
33482      */
33483     getFooterPanel : function(doShow){
33484         if(doShow){
33485             this.footerPanel.show();
33486         }
33487         return this.footerPanel;
33488     },
33489
33490     initElements : function(){
33491         var E = Roo.Element;
33492         var el = this.grid.getGridEl().dom.firstChild;
33493         var cs = el.childNodes;
33494
33495         this.el = new E(el);
33496         
33497          this.focusEl = new E(el.firstChild);
33498         this.focusEl.swallowEvent("click", true);
33499         
33500         this.headerPanel = new E(cs[1]);
33501         this.headerPanel.enableDisplayMode("block");
33502
33503         this.scroller = new E(cs[2]);
33504         this.scrollSizer = new E(this.scroller.dom.firstChild);
33505
33506         this.lockedWrap = new E(cs[3]);
33507         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33508         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33509
33510         this.mainWrap = new E(cs[4]);
33511         this.mainHd = new E(this.mainWrap.dom.firstChild);
33512         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33513
33514         this.footerPanel = new E(cs[5]);
33515         this.footerPanel.enableDisplayMode("block");
33516
33517         this.resizeProxy = new E(cs[6]);
33518
33519         this.headerSelector = String.format(
33520            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33521            this.lockedHd.id, this.mainHd.id
33522         );
33523
33524         this.splitterSelector = String.format(
33525            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33526            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33527         );
33528     },
33529     idToCssName : function(s)
33530     {
33531         return s.replace(/[^a-z0-9]+/ig, '-');
33532     },
33533
33534     getHeaderCell : function(index){
33535         return Roo.DomQuery.select(this.headerSelector)[index];
33536     },
33537
33538     getHeaderCellMeasure : function(index){
33539         return this.getHeaderCell(index).firstChild;
33540     },
33541
33542     getHeaderCellText : function(index){
33543         return this.getHeaderCell(index).firstChild.firstChild;
33544     },
33545
33546     getLockedTable : function(){
33547         return this.lockedBody.dom.firstChild;
33548     },
33549
33550     getBodyTable : function(){
33551         return this.mainBody.dom.firstChild;
33552     },
33553
33554     getLockedRow : function(index){
33555         return this.getLockedTable().rows[index];
33556     },
33557
33558     getRow : function(index){
33559         return this.getBodyTable().rows[index];
33560     },
33561
33562     getRowComposite : function(index){
33563         if(!this.rowEl){
33564             this.rowEl = new Roo.CompositeElementLite();
33565         }
33566         var els = [], lrow, mrow;
33567         if(lrow = this.getLockedRow(index)){
33568             els.push(lrow);
33569         }
33570         if(mrow = this.getRow(index)){
33571             els.push(mrow);
33572         }
33573         this.rowEl.elements = els;
33574         return this.rowEl;
33575     },
33576     /**
33577      * Gets the 'td' of the cell
33578      * 
33579      * @param {Integer} rowIndex row to select
33580      * @param {Integer} colIndex column to select
33581      * 
33582      * @return {Object} 
33583      */
33584     getCell : function(rowIndex, colIndex){
33585         var locked = this.cm.getLockedCount();
33586         var source;
33587         if(colIndex < locked){
33588             source = this.lockedBody.dom.firstChild;
33589         }else{
33590             source = this.mainBody.dom.firstChild;
33591             colIndex -= locked;
33592         }
33593         return source.rows[rowIndex].childNodes[colIndex];
33594     },
33595
33596     getCellText : function(rowIndex, colIndex){
33597         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33598     },
33599
33600     getCellBox : function(cell){
33601         var b = this.fly(cell).getBox();
33602         if(Roo.isOpera){ // opera fails to report the Y
33603             b.y = cell.offsetTop + this.mainBody.getY();
33604         }
33605         return b;
33606     },
33607
33608     getCellIndex : function(cell){
33609         var id = String(cell.className).match(this.cellRE);
33610         if(id){
33611             return parseInt(id[1], 10);
33612         }
33613         return 0;
33614     },
33615
33616     findHeaderIndex : function(n){
33617         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33618         return r ? this.getCellIndex(r) : false;
33619     },
33620
33621     findHeaderCell : function(n){
33622         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33623         return r ? r : false;
33624     },
33625
33626     findRowIndex : function(n){
33627         if(!n){
33628             return false;
33629         }
33630         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33631         return r ? r.rowIndex : false;
33632     },
33633
33634     findCellIndex : function(node){
33635         var stop = this.el.dom;
33636         while(node && node != stop){
33637             if(this.findRE.test(node.className)){
33638                 return this.getCellIndex(node);
33639             }
33640             node = node.parentNode;
33641         }
33642         return false;
33643     },
33644
33645     getColumnId : function(index){
33646         return this.cm.getColumnId(index);
33647     },
33648
33649     getSplitters : function()
33650     {
33651         if(this.splitterSelector){
33652            return Roo.DomQuery.select(this.splitterSelector);
33653         }else{
33654             return null;
33655       }
33656     },
33657
33658     getSplitter : function(index){
33659         return this.getSplitters()[index];
33660     },
33661
33662     onRowOver : function(e, t){
33663         var row;
33664         if((row = this.findRowIndex(t)) !== false){
33665             this.getRowComposite(row).addClass("x-grid-row-over");
33666         }
33667     },
33668
33669     onRowOut : function(e, t){
33670         var row;
33671         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33672             this.getRowComposite(row).removeClass("x-grid-row-over");
33673         }
33674     },
33675
33676     renderHeaders : function(){
33677         var cm = this.cm;
33678         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33679         var cb = [], lb = [], sb = [], lsb = [], p = {};
33680         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33681             p.cellId = "x-grid-hd-0-" + i;
33682             p.splitId = "x-grid-csplit-0-" + i;
33683             p.id = cm.getColumnId(i);
33684             p.value = cm.getColumnHeader(i) || "";
33685             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33686             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33687             if(!cm.isLocked(i)){
33688                 cb[cb.length] = ct.apply(p);
33689                 sb[sb.length] = st.apply(p);
33690             }else{
33691                 lb[lb.length] = ct.apply(p);
33692                 lsb[lsb.length] = st.apply(p);
33693             }
33694         }
33695         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33696                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33697     },
33698
33699     updateHeaders : function(){
33700         var html = this.renderHeaders();
33701         this.lockedHd.update(html[0]);
33702         this.mainHd.update(html[1]);
33703     },
33704
33705     /**
33706      * Focuses the specified row.
33707      * @param {Number} row The row index
33708      */
33709     focusRow : function(row)
33710     {
33711         //Roo.log('GridView.focusRow');
33712         var x = this.scroller.dom.scrollLeft;
33713         this.focusCell(row, 0, false);
33714         this.scroller.dom.scrollLeft = x;
33715     },
33716
33717     /**
33718      * Focuses the specified cell.
33719      * @param {Number} row The row index
33720      * @param {Number} col The column index
33721      * @param {Boolean} hscroll false to disable horizontal scrolling
33722      */
33723     focusCell : function(row, col, hscroll)
33724     {
33725         //Roo.log('GridView.focusCell');
33726         var el = this.ensureVisible(row, col, hscroll);
33727         this.focusEl.alignTo(el, "tl-tl");
33728         if(Roo.isGecko){
33729             this.focusEl.focus();
33730         }else{
33731             this.focusEl.focus.defer(1, this.focusEl);
33732         }
33733     },
33734
33735     /**
33736      * Scrolls the specified cell into view
33737      * @param {Number} row The row index
33738      * @param {Number} col The column index
33739      * @param {Boolean} hscroll false to disable horizontal scrolling
33740      */
33741     ensureVisible : function(row, col, hscroll)
33742     {
33743         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33744         //return null; //disable for testing.
33745         if(typeof row != "number"){
33746             row = row.rowIndex;
33747         }
33748         if(row < 0 && row >= this.ds.getCount()){
33749             return  null;
33750         }
33751         col = (col !== undefined ? col : 0);
33752         var cm = this.grid.colModel;
33753         while(cm.isHidden(col)){
33754             col++;
33755         }
33756
33757         var el = this.getCell(row, col);
33758         if(!el){
33759             return null;
33760         }
33761         var c = this.scroller.dom;
33762
33763         var ctop = parseInt(el.offsetTop, 10);
33764         var cleft = parseInt(el.offsetLeft, 10);
33765         var cbot = ctop + el.offsetHeight;
33766         var cright = cleft + el.offsetWidth;
33767         
33768         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33769         var stop = parseInt(c.scrollTop, 10);
33770         var sleft = parseInt(c.scrollLeft, 10);
33771         var sbot = stop + ch;
33772         var sright = sleft + c.clientWidth;
33773         /*
33774         Roo.log('GridView.ensureVisible:' +
33775                 ' ctop:' + ctop +
33776                 ' c.clientHeight:' + c.clientHeight +
33777                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33778                 ' stop:' + stop +
33779                 ' cbot:' + cbot +
33780                 ' sbot:' + sbot +
33781                 ' ch:' + ch  
33782                 );
33783         */
33784         if(ctop < stop){
33785             c.scrollTop = ctop;
33786             //Roo.log("set scrolltop to ctop DISABLE?");
33787         }else if(cbot > sbot){
33788             //Roo.log("set scrolltop to cbot-ch");
33789             c.scrollTop = cbot-ch;
33790         }
33791         
33792         if(hscroll !== false){
33793             if(cleft < sleft){
33794                 c.scrollLeft = cleft;
33795             }else if(cright > sright){
33796                 c.scrollLeft = cright-c.clientWidth;
33797             }
33798         }
33799          
33800         return el;
33801     },
33802
33803     updateColumns : function(){
33804         this.grid.stopEditing();
33805         var cm = this.grid.colModel, colIds = this.getColumnIds();
33806         //var totalWidth = cm.getTotalWidth();
33807         var pos = 0;
33808         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33809             //if(cm.isHidden(i)) continue;
33810             var w = cm.getColumnWidth(i);
33811             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33812             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33813         }
33814         this.updateSplitters();
33815     },
33816
33817     generateRules : function(cm){
33818         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33819         Roo.util.CSS.removeStyleSheet(rulesId);
33820         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33821             var cid = cm.getColumnId(i);
33822             var align = '';
33823             if(cm.config[i].align){
33824                 align = 'text-align:'+cm.config[i].align+';';
33825             }
33826             var hidden = '';
33827             if(cm.isHidden(i)){
33828                 hidden = 'display:none;';
33829             }
33830             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33831             ruleBuf.push(
33832                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33833                     this.hdSelector, cid, " {\n", align, width, "}\n",
33834                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33835                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33836         }
33837         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33838     },
33839
33840     updateSplitters : function(){
33841         var cm = this.cm, s = this.getSplitters();
33842         if(s){ // splitters not created yet
33843             var pos = 0, locked = true;
33844             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33845                 if(cm.isHidden(i)) {
33846                     continue;
33847                 }
33848                 var w = cm.getColumnWidth(i); // make sure it's a number
33849                 if(!cm.isLocked(i) && locked){
33850                     pos = 0;
33851                     locked = false;
33852                 }
33853                 pos += w;
33854                 s[i].style.left = (pos-this.splitOffset) + "px";
33855             }
33856         }
33857     },
33858
33859     handleHiddenChange : function(colModel, colIndex, hidden){
33860         if(hidden){
33861             this.hideColumn(colIndex);
33862         }else{
33863             this.unhideColumn(colIndex);
33864         }
33865     },
33866
33867     hideColumn : function(colIndex){
33868         var cid = this.getColumnId(colIndex);
33869         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33870         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33871         if(Roo.isSafari){
33872             this.updateHeaders();
33873         }
33874         this.updateSplitters();
33875         this.layout();
33876     },
33877
33878     unhideColumn : function(colIndex){
33879         var cid = this.getColumnId(colIndex);
33880         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33881         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33882
33883         if(Roo.isSafari){
33884             this.updateHeaders();
33885         }
33886         this.updateSplitters();
33887         this.layout();
33888     },
33889
33890     insertRows : function(dm, firstRow, lastRow, isUpdate){
33891         if(firstRow == 0 && lastRow == dm.getCount()-1){
33892             this.refresh();
33893         }else{
33894             if(!isUpdate){
33895                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33896             }
33897             var s = this.getScrollState();
33898             var markup = this.renderRows(firstRow, lastRow);
33899             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33900             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33901             this.restoreScroll(s);
33902             if(!isUpdate){
33903                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33904                 this.syncRowHeights(firstRow, lastRow);
33905                 this.stripeRows(firstRow);
33906                 this.layout();
33907             }
33908         }
33909     },
33910
33911     bufferRows : function(markup, target, index){
33912         var before = null, trows = target.rows, tbody = target.tBodies[0];
33913         if(index < trows.length){
33914             before = trows[index];
33915         }
33916         var b = document.createElement("div");
33917         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33918         var rows = b.firstChild.rows;
33919         for(var i = 0, len = rows.length; i < len; i++){
33920             if(before){
33921                 tbody.insertBefore(rows[0], before);
33922             }else{
33923                 tbody.appendChild(rows[0]);
33924             }
33925         }
33926         b.innerHTML = "";
33927         b = null;
33928     },
33929
33930     deleteRows : function(dm, firstRow, lastRow){
33931         if(dm.getRowCount()<1){
33932             this.fireEvent("beforerefresh", this);
33933             this.mainBody.update("");
33934             this.lockedBody.update("");
33935             this.fireEvent("refresh", this);
33936         }else{
33937             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33938             var bt = this.getBodyTable();
33939             var tbody = bt.firstChild;
33940             var rows = bt.rows;
33941             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33942                 tbody.removeChild(rows[firstRow]);
33943             }
33944             this.stripeRows(firstRow);
33945             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33946         }
33947     },
33948
33949     updateRows : function(dataSource, firstRow, lastRow){
33950         var s = this.getScrollState();
33951         this.refresh();
33952         this.restoreScroll(s);
33953     },
33954
33955     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33956         if(!noRefresh){
33957            this.refresh();
33958         }
33959         this.updateHeaderSortState();
33960     },
33961
33962     getScrollState : function(){
33963         
33964         var sb = this.scroller.dom;
33965         return {left: sb.scrollLeft, top: sb.scrollTop};
33966     },
33967
33968     stripeRows : function(startRow){
33969         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33970             return;
33971         }
33972         startRow = startRow || 0;
33973         var rows = this.getBodyTable().rows;
33974         var lrows = this.getLockedTable().rows;
33975         var cls = ' x-grid-row-alt ';
33976         for(var i = startRow, len = rows.length; i < len; i++){
33977             var row = rows[i], lrow = lrows[i];
33978             var isAlt = ((i+1) % 2 == 0);
33979             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33980             if(isAlt == hasAlt){
33981                 continue;
33982             }
33983             if(isAlt){
33984                 row.className += " x-grid-row-alt";
33985             }else{
33986                 row.className = row.className.replace("x-grid-row-alt", "");
33987             }
33988             if(lrow){
33989                 lrow.className = row.className;
33990             }
33991         }
33992     },
33993
33994     restoreScroll : function(state){
33995         //Roo.log('GridView.restoreScroll');
33996         var sb = this.scroller.dom;
33997         sb.scrollLeft = state.left;
33998         sb.scrollTop = state.top;
33999         this.syncScroll();
34000     },
34001
34002     syncScroll : function(){
34003         //Roo.log('GridView.syncScroll');
34004         var sb = this.scroller.dom;
34005         var sh = this.mainHd.dom;
34006         var bs = this.mainBody.dom;
34007         var lv = this.lockedBody.dom;
34008         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34009         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34010     },
34011
34012     handleScroll : function(e){
34013         this.syncScroll();
34014         var sb = this.scroller.dom;
34015         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34016         e.stopEvent();
34017     },
34018
34019     handleWheel : function(e){
34020         var d = e.getWheelDelta();
34021         this.scroller.dom.scrollTop -= d*22;
34022         // set this here to prevent jumpy scrolling on large tables
34023         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34024         e.stopEvent();
34025     },
34026
34027     renderRows : function(startRow, endRow){
34028         // pull in all the crap needed to render rows
34029         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34030         var colCount = cm.getColumnCount();
34031
34032         if(ds.getCount() < 1){
34033             return ["", ""];
34034         }
34035
34036         // build a map for all the columns
34037         var cs = [];
34038         for(var i = 0; i < colCount; i++){
34039             var name = cm.getDataIndex(i);
34040             cs[i] = {
34041                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34042                 renderer : cm.getRenderer(i),
34043                 id : cm.getColumnId(i),
34044                 locked : cm.isLocked(i),
34045                 has_editor : cm.isCellEditable(i)
34046             };
34047         }
34048
34049         startRow = startRow || 0;
34050         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34051
34052         // records to render
34053         var rs = ds.getRange(startRow, endRow);
34054
34055         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34056     },
34057
34058     // As much as I hate to duplicate code, this was branched because FireFox really hates
34059     // [].join("") on strings. The performance difference was substantial enough to
34060     // branch this function
34061     doRender : Roo.isGecko ?
34062             function(cs, rs, ds, startRow, colCount, stripe){
34063                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34064                 // buffers
34065                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34066                 
34067                 var hasListener = this.grid.hasListener('rowclass');
34068                 var rowcfg = {};
34069                 for(var j = 0, len = rs.length; j < len; j++){
34070                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34071                     for(var i = 0; i < colCount; i++){
34072                         c = cs[i];
34073                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34074                         p.id = c.id;
34075                         p.css = p.attr = "";
34076                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34077                         if(p.value == undefined || p.value === "") {
34078                             p.value = "&#160;";
34079                         }
34080                         if(c.has_editor){
34081                             p.css += ' x-grid-editable-cell';
34082                         }
34083                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34084                             p.css +=  ' x-grid-dirty-cell';
34085                         }
34086                         var markup = ct.apply(p);
34087                         if(!c.locked){
34088                             cb+= markup;
34089                         }else{
34090                             lcb+= markup;
34091                         }
34092                     }
34093                     var alt = [];
34094                     if(stripe && ((rowIndex+1) % 2 == 0)){
34095                         alt.push("x-grid-row-alt")
34096                     }
34097                     if(r.dirty){
34098                         alt.push(  " x-grid-dirty-row");
34099                     }
34100                     rp.cells = lcb;
34101                     if(this.getRowClass){
34102                         alt.push(this.getRowClass(r, rowIndex));
34103                     }
34104                     if (hasListener) {
34105                         rowcfg = {
34106                              
34107                             record: r,
34108                             rowIndex : rowIndex,
34109                             rowClass : ''
34110                         };
34111                         this.grid.fireEvent('rowclass', this, rowcfg);
34112                         alt.push(rowcfg.rowClass);
34113                     }
34114                     rp.alt = alt.join(" ");
34115                     lbuf+= rt.apply(rp);
34116                     rp.cells = cb;
34117                     buf+=  rt.apply(rp);
34118                 }
34119                 return [lbuf, buf];
34120             } :
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                 var hasListener = this.grid.hasListener('rowclass');
34126  
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                         //Roo.log(c);
34140                          if(c.has_editor){
34141                             p.css += ' x-grid-editable-cell';
34142                         }
34143                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34144                             p.css += ' x-grid-dirty-cell' 
34145                         }
34146                         
34147                         var markup = ct.apply(p);
34148                         if(!c.locked){
34149                             cb[cb.length] = markup;
34150                         }else{
34151                             lcb[lcb.length] = markup;
34152                         }
34153                     }
34154                     var alt = [];
34155                     if(stripe && ((rowIndex+1) % 2 == 0)){
34156                         alt.push( "x-grid-row-alt");
34157                     }
34158                     if(r.dirty){
34159                         alt.push(" x-grid-dirty-row");
34160                     }
34161                     rp.cells = lcb;
34162                     if(this.getRowClass){
34163                         alt.push( this.getRowClass(r, rowIndex));
34164                     }
34165                     if (hasListener) {
34166                         rowcfg = {
34167                              
34168                             record: r,
34169                             rowIndex : rowIndex,
34170                             rowClass : ''
34171                         };
34172                         this.grid.fireEvent('rowclass', this, rowcfg);
34173                         alt.push(rowcfg.rowClass);
34174                     }
34175                     
34176                     rp.alt = alt.join(" ");
34177                     rp.cells = lcb.join("");
34178                     lbuf[lbuf.length] = rt.apply(rp);
34179                     rp.cells = cb.join("");
34180                     buf[buf.length] =  rt.apply(rp);
34181                 }
34182                 return [lbuf.join(""), buf.join("")];
34183             },
34184
34185     renderBody : function(){
34186         var markup = this.renderRows();
34187         var bt = this.templates.body;
34188         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34189     },
34190
34191     /**
34192      * Refreshes the grid
34193      * @param {Boolean} headersToo
34194      */
34195     refresh : function(headersToo){
34196         this.fireEvent("beforerefresh", this);
34197         this.grid.stopEditing();
34198         var result = this.renderBody();
34199         this.lockedBody.update(result[0]);
34200         this.mainBody.update(result[1]);
34201         if(headersToo === true){
34202             this.updateHeaders();
34203             this.updateColumns();
34204             this.updateSplitters();
34205             this.updateHeaderSortState();
34206         }
34207         this.syncRowHeights();
34208         this.layout();
34209         this.fireEvent("refresh", this);
34210     },
34211
34212     handleColumnMove : function(cm, oldIndex, newIndex){
34213         this.indexMap = null;
34214         var s = this.getScrollState();
34215         this.refresh(true);
34216         this.restoreScroll(s);
34217         this.afterMove(newIndex);
34218     },
34219
34220     afterMove : function(colIndex){
34221         if(this.enableMoveAnim && Roo.enableFx){
34222             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34223         }
34224         // if multisort - fix sortOrder, and reload..
34225         if (this.grid.dataSource.multiSort) {
34226             // the we can call sort again..
34227             var dm = this.grid.dataSource;
34228             var cm = this.grid.colModel;
34229             var so = [];
34230             for(var i = 0; i < cm.config.length; i++ ) {
34231                 
34232                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34233                     continue; // dont' bother, it's not in sort list or being set.
34234                 }
34235                 
34236                 so.push(cm.config[i].dataIndex);
34237             };
34238             dm.sortOrder = so;
34239             dm.load(dm.lastOptions);
34240             
34241             
34242         }
34243         
34244     },
34245
34246     updateCell : function(dm, rowIndex, dataIndex){
34247         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34248         if(typeof colIndex == "undefined"){ // not present in grid
34249             return;
34250         }
34251         var cm = this.grid.colModel;
34252         var cell = this.getCell(rowIndex, colIndex);
34253         var cellText = this.getCellText(rowIndex, colIndex);
34254
34255         var p = {
34256             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34257             id : cm.getColumnId(colIndex),
34258             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34259         };
34260         var renderer = cm.getRenderer(colIndex);
34261         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34262         if(typeof val == "undefined" || val === "") {
34263             val = "&#160;";
34264         }
34265         cellText.innerHTML = val;
34266         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34267         this.syncRowHeights(rowIndex, rowIndex);
34268     },
34269
34270     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34271         var maxWidth = 0;
34272         if(this.grid.autoSizeHeaders){
34273             var h = this.getHeaderCellMeasure(colIndex);
34274             maxWidth = Math.max(maxWidth, h.scrollWidth);
34275         }
34276         var tb, index;
34277         if(this.cm.isLocked(colIndex)){
34278             tb = this.getLockedTable();
34279             index = colIndex;
34280         }else{
34281             tb = this.getBodyTable();
34282             index = colIndex - this.cm.getLockedCount();
34283         }
34284         if(tb && tb.rows){
34285             var rows = tb.rows;
34286             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34287             for(var i = 0; i < stopIndex; i++){
34288                 var cell = rows[i].childNodes[index].firstChild;
34289                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34290             }
34291         }
34292         return maxWidth + /*margin for error in IE*/ 5;
34293     },
34294     /**
34295      * Autofit a column to its content.
34296      * @param {Number} colIndex
34297      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34298      */
34299      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34300          if(this.cm.isHidden(colIndex)){
34301              return; // can't calc a hidden column
34302          }
34303         if(forceMinSize){
34304             var cid = this.cm.getColumnId(colIndex);
34305             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34306            if(this.grid.autoSizeHeaders){
34307                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34308            }
34309         }
34310         var newWidth = this.calcColumnWidth(colIndex);
34311         this.cm.setColumnWidth(colIndex,
34312             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34313         if(!suppressEvent){
34314             this.grid.fireEvent("columnresize", colIndex, newWidth);
34315         }
34316     },
34317
34318     /**
34319      * Autofits all columns to their content and then expands to fit any extra space in the grid
34320      */
34321      autoSizeColumns : function(){
34322         var cm = this.grid.colModel;
34323         var colCount = cm.getColumnCount();
34324         for(var i = 0; i < colCount; i++){
34325             this.autoSizeColumn(i, true, true);
34326         }
34327         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34328             this.fitColumns();
34329         }else{
34330             this.updateColumns();
34331             this.layout();
34332         }
34333     },
34334
34335     /**
34336      * Autofits all columns to the grid's width proportionate with their current size
34337      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34338      */
34339     fitColumns : function(reserveScrollSpace){
34340         var cm = this.grid.colModel;
34341         var colCount = cm.getColumnCount();
34342         var cols = [];
34343         var width = 0;
34344         var i, w;
34345         for (i = 0; i < colCount; i++){
34346             if(!cm.isHidden(i) && !cm.isFixed(i)){
34347                 w = cm.getColumnWidth(i);
34348                 cols.push(i);
34349                 cols.push(w);
34350                 width += w;
34351             }
34352         }
34353         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34354         if(reserveScrollSpace){
34355             avail -= 17;
34356         }
34357         var frac = (avail - cm.getTotalWidth())/width;
34358         while (cols.length){
34359             w = cols.pop();
34360             i = cols.pop();
34361             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34362         }
34363         this.updateColumns();
34364         this.layout();
34365     },
34366
34367     onRowSelect : function(rowIndex){
34368         var row = this.getRowComposite(rowIndex);
34369         row.addClass("x-grid-row-selected");
34370     },
34371
34372     onRowDeselect : function(rowIndex){
34373         var row = this.getRowComposite(rowIndex);
34374         row.removeClass("x-grid-row-selected");
34375     },
34376
34377     onCellSelect : function(row, col){
34378         var cell = this.getCell(row, col);
34379         if(cell){
34380             Roo.fly(cell).addClass("x-grid-cell-selected");
34381         }
34382     },
34383
34384     onCellDeselect : function(row, col){
34385         var cell = this.getCell(row, col);
34386         if(cell){
34387             Roo.fly(cell).removeClass("x-grid-cell-selected");
34388         }
34389     },
34390
34391     updateHeaderSortState : function(){
34392         
34393         // sort state can be single { field: xxx, direction : yyy}
34394         // or   { xxx=>ASC , yyy : DESC ..... }
34395         
34396         var mstate = {};
34397         if (!this.ds.multiSort) { 
34398             var state = this.ds.getSortState();
34399             if(!state){
34400                 return;
34401             }
34402             mstate[state.field] = state.direction;
34403             // FIXME... - this is not used here.. but might be elsewhere..
34404             this.sortState = state;
34405             
34406         } else {
34407             mstate = this.ds.sortToggle;
34408         }
34409         //remove existing sort classes..
34410         
34411         var sc = this.sortClasses;
34412         var hds = this.el.select(this.headerSelector).removeClass(sc);
34413         
34414         for(var f in mstate) {
34415         
34416             var sortColumn = this.cm.findColumnIndex(f);
34417             
34418             if(sortColumn != -1){
34419                 var sortDir = mstate[f];        
34420                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34421             }
34422         }
34423         
34424          
34425         
34426     },
34427
34428
34429     handleHeaderClick : function(g, index,e){
34430         
34431         Roo.log("header click");
34432         
34433         if (Roo.isTouch) {
34434             // touch events on header are handled by context
34435             this.handleHdCtx(g,index,e);
34436             return;
34437         }
34438         
34439         
34440         if(this.headersDisabled){
34441             return;
34442         }
34443         var dm = g.dataSource, cm = g.colModel;
34444         if(!cm.isSortable(index)){
34445             return;
34446         }
34447         g.stopEditing();
34448         
34449         if (dm.multiSort) {
34450             // update the sortOrder
34451             var so = [];
34452             for(var i = 0; i < cm.config.length; i++ ) {
34453                 
34454                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34455                     continue; // dont' bother, it's not in sort list or being set.
34456                 }
34457                 
34458                 so.push(cm.config[i].dataIndex);
34459             };
34460             dm.sortOrder = so;
34461         }
34462         
34463         
34464         dm.sort(cm.getDataIndex(index));
34465     },
34466
34467
34468     destroy : function(){
34469         if(this.colMenu){
34470             this.colMenu.removeAll();
34471             Roo.menu.MenuMgr.unregister(this.colMenu);
34472             this.colMenu.getEl().remove();
34473             delete this.colMenu;
34474         }
34475         if(this.hmenu){
34476             this.hmenu.removeAll();
34477             Roo.menu.MenuMgr.unregister(this.hmenu);
34478             this.hmenu.getEl().remove();
34479             delete this.hmenu;
34480         }
34481         if(this.grid.enableColumnMove){
34482             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34483             if(dds){
34484                 for(var dd in dds){
34485                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34486                         var elid = dds[dd].dragElId;
34487                         dds[dd].unreg();
34488                         Roo.get(elid).remove();
34489                     } else if(dds[dd].config.isTarget){
34490                         dds[dd].proxyTop.remove();
34491                         dds[dd].proxyBottom.remove();
34492                         dds[dd].unreg();
34493                     }
34494                     if(Roo.dd.DDM.locationCache[dd]){
34495                         delete Roo.dd.DDM.locationCache[dd];
34496                     }
34497                 }
34498                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34499             }
34500         }
34501         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34502         this.bind(null, null);
34503         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34504     },
34505
34506     handleLockChange : function(){
34507         this.refresh(true);
34508     },
34509
34510     onDenyColumnLock : function(){
34511
34512     },
34513
34514     onDenyColumnHide : function(){
34515
34516     },
34517
34518     handleHdMenuClick : function(item){
34519         var index = this.hdCtxIndex;
34520         var cm = this.cm, ds = this.ds;
34521         switch(item.id){
34522             case "asc":
34523                 ds.sort(cm.getDataIndex(index), "ASC");
34524                 break;
34525             case "desc":
34526                 ds.sort(cm.getDataIndex(index), "DESC");
34527                 break;
34528             case "lock":
34529                 var lc = cm.getLockedCount();
34530                 if(cm.getColumnCount(true) <= lc+1){
34531                     this.onDenyColumnLock();
34532                     return;
34533                 }
34534                 if(lc != index){
34535                     cm.setLocked(index, true, true);
34536                     cm.moveColumn(index, lc);
34537                     this.grid.fireEvent("columnmove", index, lc);
34538                 }else{
34539                     cm.setLocked(index, true);
34540                 }
34541             break;
34542             case "unlock":
34543                 var lc = cm.getLockedCount();
34544                 if((lc-1) != index){
34545                     cm.setLocked(index, false, true);
34546                     cm.moveColumn(index, lc-1);
34547                     this.grid.fireEvent("columnmove", index, lc-1);
34548                 }else{
34549                     cm.setLocked(index, false);
34550                 }
34551             break;
34552             case 'wider': // used to expand cols on touch..
34553             case 'narrow':
34554                 var cw = cm.getColumnWidth(index);
34555                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34556                 cw = Math.max(0, cw);
34557                 cw = Math.min(cw,4000);
34558                 cm.setColumnWidth(index, cw);
34559                 break;
34560                 
34561             default:
34562                 index = cm.getIndexById(item.id.substr(4));
34563                 if(index != -1){
34564                     if(item.checked && cm.getColumnCount(true) <= 1){
34565                         this.onDenyColumnHide();
34566                         return false;
34567                     }
34568                     cm.setHidden(index, item.checked);
34569                 }
34570         }
34571         return true;
34572     },
34573
34574     beforeColMenuShow : function(){
34575         var cm = this.cm,  colCount = cm.getColumnCount();
34576         this.colMenu.removeAll();
34577         for(var i = 0; i < colCount; i++){
34578             this.colMenu.add(new Roo.menu.CheckItem({
34579                 id: "col-"+cm.getColumnId(i),
34580                 text: cm.getColumnHeader(i),
34581                 checked: !cm.isHidden(i),
34582                 hideOnClick:false
34583             }));
34584         }
34585     },
34586
34587     handleHdCtx : function(g, index, e){
34588         e.stopEvent();
34589         var hd = this.getHeaderCell(index);
34590         this.hdCtxIndex = index;
34591         var ms = this.hmenu.items, cm = this.cm;
34592         ms.get("asc").setDisabled(!cm.isSortable(index));
34593         ms.get("desc").setDisabled(!cm.isSortable(index));
34594         if(this.grid.enableColLock !== false){
34595             ms.get("lock").setDisabled(cm.isLocked(index));
34596             ms.get("unlock").setDisabled(!cm.isLocked(index));
34597         }
34598         this.hmenu.show(hd, "tl-bl");
34599     },
34600
34601     handleHdOver : function(e){
34602         var hd = this.findHeaderCell(e.getTarget());
34603         if(hd && !this.headersDisabled){
34604             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34605                this.fly(hd).addClass("x-grid-hd-over");
34606             }
34607         }
34608     },
34609
34610     handleHdOut : function(e){
34611         var hd = this.findHeaderCell(e.getTarget());
34612         if(hd){
34613             this.fly(hd).removeClass("x-grid-hd-over");
34614         }
34615     },
34616
34617     handleSplitDblClick : function(e, t){
34618         var i = this.getCellIndex(t);
34619         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34620             this.autoSizeColumn(i, true);
34621             this.layout();
34622         }
34623     },
34624
34625     render : function(){
34626
34627         var cm = this.cm;
34628         var colCount = cm.getColumnCount();
34629
34630         if(this.grid.monitorWindowResize === true){
34631             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34632         }
34633         var header = this.renderHeaders();
34634         var body = this.templates.body.apply({rows:""});
34635         var html = this.templates.master.apply({
34636             lockedBody: body,
34637             body: body,
34638             lockedHeader: header[0],
34639             header: header[1]
34640         });
34641
34642         //this.updateColumns();
34643
34644         this.grid.getGridEl().dom.innerHTML = html;
34645
34646         this.initElements();
34647         
34648         // a kludge to fix the random scolling effect in webkit
34649         this.el.on("scroll", function() {
34650             this.el.dom.scrollTop=0; // hopefully not recursive..
34651         },this);
34652
34653         this.scroller.on("scroll", this.handleScroll, this);
34654         this.lockedBody.on("mousewheel", this.handleWheel, this);
34655         this.mainBody.on("mousewheel", this.handleWheel, this);
34656
34657         this.mainHd.on("mouseover", this.handleHdOver, this);
34658         this.mainHd.on("mouseout", this.handleHdOut, this);
34659         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34660                 {delegate: "."+this.splitClass});
34661
34662         this.lockedHd.on("mouseover", this.handleHdOver, this);
34663         this.lockedHd.on("mouseout", this.handleHdOut, this);
34664         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34665                 {delegate: "."+this.splitClass});
34666
34667         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34668             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34669         }
34670
34671         this.updateSplitters();
34672
34673         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34674             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34675             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34676         }
34677
34678         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34679             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34680             this.hmenu.add(
34681                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34682                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34683             );
34684             if(this.grid.enableColLock !== false){
34685                 this.hmenu.add('-',
34686                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34687                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34688                 );
34689             }
34690             if (Roo.isTouch) {
34691                  this.hmenu.add('-',
34692                     {id:"wider", text: this.columnsWiderText},
34693                     {id:"narrow", text: this.columnsNarrowText }
34694                 );
34695                 
34696                  
34697             }
34698             
34699             if(this.grid.enableColumnHide !== false){
34700
34701                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34702                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34703                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34704
34705                 this.hmenu.add('-',
34706                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34707                 );
34708             }
34709             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34710
34711             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34712         }
34713
34714         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34715             this.dd = new Roo.grid.GridDragZone(this.grid, {
34716                 ddGroup : this.grid.ddGroup || 'GridDD'
34717             });
34718             
34719         }
34720
34721         /*
34722         for(var i = 0; i < colCount; i++){
34723             if(cm.isHidden(i)){
34724                 this.hideColumn(i);
34725             }
34726             if(cm.config[i].align){
34727                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34728                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34729             }
34730         }*/
34731         
34732         this.updateHeaderSortState();
34733
34734         this.beforeInitialResize();
34735         this.layout(true);
34736
34737         // two part rendering gives faster view to the user
34738         this.renderPhase2.defer(1, this);
34739     },
34740
34741     renderPhase2 : function(){
34742         // render the rows now
34743         this.refresh();
34744         if(this.grid.autoSizeColumns){
34745             this.autoSizeColumns();
34746         }
34747     },
34748
34749     beforeInitialResize : function(){
34750
34751     },
34752
34753     onColumnSplitterMoved : function(i, w){
34754         this.userResized = true;
34755         var cm = this.grid.colModel;
34756         cm.setColumnWidth(i, w, true);
34757         var cid = cm.getColumnId(i);
34758         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34759         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34760         this.updateSplitters();
34761         this.layout();
34762         this.grid.fireEvent("columnresize", i, w);
34763     },
34764
34765     syncRowHeights : function(startIndex, endIndex){
34766         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34767             startIndex = startIndex || 0;
34768             var mrows = this.getBodyTable().rows;
34769             var lrows = this.getLockedTable().rows;
34770             var len = mrows.length-1;
34771             endIndex = Math.min(endIndex || len, len);
34772             for(var i = startIndex; i <= endIndex; i++){
34773                 var m = mrows[i], l = lrows[i];
34774                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34775                 m.style.height = l.style.height = h + "px";
34776             }
34777         }
34778     },
34779
34780     layout : function(initialRender, is2ndPass)
34781     {
34782         var g = this.grid;
34783         var auto = g.autoHeight;
34784         var scrollOffset = 16;
34785         var c = g.getGridEl(), cm = this.cm,
34786                 expandCol = g.autoExpandColumn,
34787                 gv = this;
34788         //c.beginMeasure();
34789
34790         if(!c.dom.offsetWidth){ // display:none?
34791             if(initialRender){
34792                 this.lockedWrap.show();
34793                 this.mainWrap.show();
34794             }
34795             return;
34796         }
34797
34798         var hasLock = this.cm.isLocked(0);
34799
34800         var tbh = this.headerPanel.getHeight();
34801         var bbh = this.footerPanel.getHeight();
34802
34803         if(auto){
34804             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34805             var newHeight = ch + c.getBorderWidth("tb");
34806             if(g.maxHeight){
34807                 newHeight = Math.min(g.maxHeight, newHeight);
34808             }
34809             c.setHeight(newHeight);
34810         }
34811
34812         if(g.autoWidth){
34813             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34814         }
34815
34816         var s = this.scroller;
34817
34818         var csize = c.getSize(true);
34819
34820         this.el.setSize(csize.width, csize.height);
34821
34822         this.headerPanel.setWidth(csize.width);
34823         this.footerPanel.setWidth(csize.width);
34824
34825         var hdHeight = this.mainHd.getHeight();
34826         var vw = csize.width;
34827         var vh = csize.height - (tbh + bbh);
34828
34829         s.setSize(vw, vh);
34830
34831         var bt = this.getBodyTable();
34832         
34833         if(cm.getLockedCount() == cm.config.length){
34834             bt = this.getLockedTable();
34835         }
34836         
34837         var ltWidth = hasLock ?
34838                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34839
34840         var scrollHeight = bt.offsetHeight;
34841         var scrollWidth = ltWidth + bt.offsetWidth;
34842         var vscroll = false, hscroll = false;
34843
34844         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34845
34846         var lw = this.lockedWrap, mw = this.mainWrap;
34847         var lb = this.lockedBody, mb = this.mainBody;
34848
34849         setTimeout(function(){
34850             var t = s.dom.offsetTop;
34851             var w = s.dom.clientWidth,
34852                 h = s.dom.clientHeight;
34853
34854             lw.setTop(t);
34855             lw.setSize(ltWidth, h);
34856
34857             mw.setLeftTop(ltWidth, t);
34858             mw.setSize(w-ltWidth, h);
34859
34860             lb.setHeight(h-hdHeight);
34861             mb.setHeight(h-hdHeight);
34862
34863             if(is2ndPass !== true && !gv.userResized && expandCol){
34864                 // high speed resize without full column calculation
34865                 
34866                 var ci = cm.getIndexById(expandCol);
34867                 if (ci < 0) {
34868                     ci = cm.findColumnIndex(expandCol);
34869                 }
34870                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34871                 var expandId = cm.getColumnId(ci);
34872                 var  tw = cm.getTotalWidth(false);
34873                 var currentWidth = cm.getColumnWidth(ci);
34874                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34875                 if(currentWidth != cw){
34876                     cm.setColumnWidth(ci, cw, true);
34877                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34878                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34879                     gv.updateSplitters();
34880                     gv.layout(false, true);
34881                 }
34882             }
34883
34884             if(initialRender){
34885                 lw.show();
34886                 mw.show();
34887             }
34888             //c.endMeasure();
34889         }, 10);
34890     },
34891
34892     onWindowResize : function(){
34893         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34894             return;
34895         }
34896         this.layout();
34897     },
34898
34899     appendFooter : function(parentEl){
34900         return null;
34901     },
34902
34903     sortAscText : "Sort Ascending",
34904     sortDescText : "Sort Descending",
34905     lockText : "Lock Column",
34906     unlockText : "Unlock Column",
34907     columnsText : "Columns",
34908  
34909     columnsWiderText : "Wider",
34910     columnsNarrowText : "Thinner"
34911 });
34912
34913
34914 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34915     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34916     this.proxy.el.addClass('x-grid3-col-dd');
34917 };
34918
34919 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34920     handleMouseDown : function(e){
34921
34922     },
34923
34924     callHandleMouseDown : function(e){
34925         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34926     }
34927 });
34928 /*
34929  * Based on:
34930  * Ext JS Library 1.1.1
34931  * Copyright(c) 2006-2007, Ext JS, LLC.
34932  *
34933  * Originally Released Under LGPL - original licence link has changed is not relivant.
34934  *
34935  * Fork - LGPL
34936  * <script type="text/javascript">
34937  */
34938  /**
34939  * @extends Roo.dd.DDProxy
34940  * @class Roo.grid.SplitDragZone
34941  * Support for Column Header resizing
34942  * @constructor
34943  * @param {Object} config
34944  */
34945 // private
34946 // This is a support class used internally by the Grid components
34947 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34948     this.grid = grid;
34949     this.view = grid.getView();
34950     this.proxy = this.view.resizeProxy;
34951     Roo.grid.SplitDragZone.superclass.constructor.call(
34952         this,
34953         hd, // ID
34954         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
34955         {  // CONFIG
34956             dragElId : Roo.id(this.proxy.dom),
34957             resizeFrame:false
34958         }
34959     );
34960     
34961     this.setHandleElId(Roo.id(hd));
34962     if (hd2 !== false) {
34963         this.setOuterHandleElId(Roo.id(hd2));
34964     }
34965     
34966     this.scroll = false;
34967 };
34968 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34969     fly: Roo.Element.fly,
34970
34971     b4StartDrag : function(x, y){
34972         this.view.headersDisabled = true;
34973         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
34974                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
34975         );
34976         this.proxy.setHeight(h);
34977         
34978         // for old system colWidth really stored the actual width?
34979         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
34980         // which in reality did not work.. - it worked only for fixed sizes
34981         // for resizable we need to use actual sizes.
34982         var w = this.cm.getColumnWidth(this.cellIndex);
34983         if (!this.view.mainWrap) {
34984             // bootstrap.
34985             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
34986         }
34987         
34988         
34989         
34990         // this was w-this.grid.minColumnWidth;
34991         // doesnt really make sense? - w = thie curren width or the rendered one?
34992         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34993         this.resetConstraints();
34994         this.setXConstraint(minw, 1000);
34995         this.setYConstraint(0, 0);
34996         this.minX = x - minw;
34997         this.maxX = x + 1000;
34998         this.startPos = x;
34999         if (!this.view.mainWrap) { // this is Bootstrap code..
35000             this.getDragEl().style.display='block';
35001         }
35002         
35003         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35004     },
35005
35006
35007     handleMouseDown : function(e){
35008         ev = Roo.EventObject.setEvent(e);
35009         var t = this.fly(ev.getTarget());
35010         if(t.hasClass("x-grid-split")){
35011             this.cellIndex = this.view.getCellIndex(t.dom);
35012             this.split = t.dom;
35013             this.cm = this.grid.colModel;
35014             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35015                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35016             }
35017         }
35018     },
35019
35020     endDrag : function(e){
35021         this.view.headersDisabled = false;
35022         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35023         var diff = endX - this.startPos;
35024         // 
35025         var w = this.cm.getColumnWidth(this.cellIndex);
35026         if (!this.view.mainWrap) {
35027             w = 0;
35028         }
35029         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35030     },
35031
35032     autoOffset : function(){
35033         this.setDelta(0,0);
35034     }
35035 });/*
35036  * Based on:
35037  * Ext JS Library 1.1.1
35038  * Copyright(c) 2006-2007, Ext JS, LLC.
35039  *
35040  * Originally Released Under LGPL - original licence link has changed is not relivant.
35041  *
35042  * Fork - LGPL
35043  * <script type="text/javascript">
35044  */
35045  
35046 // private
35047 // This is a support class used internally by the Grid components
35048 Roo.grid.GridDragZone = function(grid, config){
35049     this.view = grid.getView();
35050     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35051     if(this.view.lockedBody){
35052         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35053         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35054     }
35055     this.scroll = false;
35056     this.grid = grid;
35057     this.ddel = document.createElement('div');
35058     this.ddel.className = 'x-grid-dd-wrap';
35059 };
35060
35061 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35062     ddGroup : "GridDD",
35063
35064     getDragData : function(e){
35065         var t = Roo.lib.Event.getTarget(e);
35066         var rowIndex = this.view.findRowIndex(t);
35067         var sm = this.grid.selModel;
35068             
35069         //Roo.log(rowIndex);
35070         
35071         if (sm.getSelectedCell) {
35072             // cell selection..
35073             if (!sm.getSelectedCell()) {
35074                 return false;
35075             }
35076             if (rowIndex != sm.getSelectedCell()[0]) {
35077                 return false;
35078             }
35079         
35080         }
35081         if (sm.getSelections && sm.getSelections().length < 1) {
35082             return false;
35083         }
35084         
35085         
35086         // before it used to all dragging of unseleted... - now we dont do that.
35087         if(rowIndex !== false){
35088             
35089             // if editorgrid.. 
35090             
35091             
35092             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35093                
35094             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35095               //  
35096             //}
35097             if (e.hasModifier()){
35098                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35099             }
35100             
35101             Roo.log("getDragData");
35102             
35103             return {
35104                 grid: this.grid,
35105                 ddel: this.ddel,
35106                 rowIndex: rowIndex,
35107                 selections: sm.getSelections ? sm.getSelections() : (
35108                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35109             };
35110         }
35111         return false;
35112     },
35113     
35114     
35115     onInitDrag : function(e){
35116         var data = this.dragData;
35117         this.ddel.innerHTML = this.grid.getDragDropText();
35118         this.proxy.update(this.ddel);
35119         // fire start drag?
35120     },
35121
35122     afterRepair : function(){
35123         this.dragging = false;
35124     },
35125
35126     getRepairXY : function(e, data){
35127         return false;
35128     },
35129
35130     onEndDrag : function(data, e){
35131         // fire end drag?
35132     },
35133
35134     onValidDrop : function(dd, e, id){
35135         // fire drag drop?
35136         this.hideProxy();
35137     },
35138
35139     beforeInvalidDrop : function(e, id){
35140
35141     }
35142 });/*
35143  * Based on:
35144  * Ext JS Library 1.1.1
35145  * Copyright(c) 2006-2007, Ext JS, LLC.
35146  *
35147  * Originally Released Under LGPL - original licence link has changed is not relivant.
35148  *
35149  * Fork - LGPL
35150  * <script type="text/javascript">
35151  */
35152  
35153
35154 /**
35155  * @class Roo.grid.ColumnModel
35156  * @extends Roo.util.Observable
35157  * This is the default implementation of a ColumnModel used by the Grid. It defines
35158  * the columns in the grid.
35159  * <br>Usage:<br>
35160  <pre><code>
35161  var colModel = new Roo.grid.ColumnModel([
35162         {header: "Ticker", width: 60, sortable: true, locked: true},
35163         {header: "Company Name", width: 150, sortable: true},
35164         {header: "Market Cap.", width: 100, sortable: true},
35165         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35166         {header: "Employees", width: 100, sortable: true, resizable: false}
35167  ]);
35168  </code></pre>
35169  * <p>
35170  
35171  * The config options listed for this class are options which may appear in each
35172  * individual column definition.
35173  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35174  * @constructor
35175  * @param {Object} config An Array of column config objects. See this class's
35176  * config objects for details.
35177 */
35178 Roo.grid.ColumnModel = function(config){
35179         /**
35180      * The config passed into the constructor
35181      */
35182     this.config = []; //config;
35183     this.lookup = {};
35184
35185     // if no id, create one
35186     // if the column does not have a dataIndex mapping,
35187     // map it to the order it is in the config
35188     for(var i = 0, len = config.length; i < len; i++){
35189         this.addColumn(config[i]);
35190         
35191     }
35192
35193     /**
35194      * The width of columns which have no width specified (defaults to 100)
35195      * @type Number
35196      */
35197     this.defaultWidth = 100;
35198
35199     /**
35200      * Default sortable of columns which have no sortable specified (defaults to false)
35201      * @type Boolean
35202      */
35203     this.defaultSortable = false;
35204
35205     this.addEvents({
35206         /**
35207              * @event widthchange
35208              * Fires when the width of a column changes.
35209              * @param {ColumnModel} this
35210              * @param {Number} columnIndex The column index
35211              * @param {Number} newWidth The new width
35212              */
35213             "widthchange": true,
35214         /**
35215              * @event headerchange
35216              * Fires when the text of a header changes.
35217              * @param {ColumnModel} this
35218              * @param {Number} columnIndex The column index
35219              * @param {Number} newText The new header text
35220              */
35221             "headerchange": true,
35222         /**
35223              * @event hiddenchange
35224              * Fires when a column is hidden or "unhidden".
35225              * @param {ColumnModel} this
35226              * @param {Number} columnIndex The column index
35227              * @param {Boolean} hidden true if hidden, false otherwise
35228              */
35229             "hiddenchange": true,
35230             /**
35231          * @event columnmoved
35232          * Fires when a column is moved.
35233          * @param {ColumnModel} this
35234          * @param {Number} oldIndex
35235          * @param {Number} newIndex
35236          */
35237         "columnmoved" : true,
35238         /**
35239          * @event columlockchange
35240          * Fires when a column's locked state is changed
35241          * @param {ColumnModel} this
35242          * @param {Number} colIndex
35243          * @param {Boolean} locked true if locked
35244          */
35245         "columnlockchange" : true
35246     });
35247     Roo.grid.ColumnModel.superclass.constructor.call(this);
35248 };
35249 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35250     /**
35251      * @cfg {String} header The header text to display in the Grid view.
35252      */
35253         /**
35254      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35255      */
35256         /**
35257      * @cfg {String} smHeader Header at Bootsrap Small width
35258      */
35259         /**
35260      * @cfg {String} mdHeader Header at Bootsrap Medium width
35261      */
35262         /**
35263      * @cfg {String} lgHeader Header at Bootsrap Large width
35264      */
35265         /**
35266      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35267      */
35268     /**
35269      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35270      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35271      * specified, the column's index is used as an index into the Record's data Array.
35272      */
35273     /**
35274      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35275      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35276      */
35277     /**
35278      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35279      * Defaults to the value of the {@link #defaultSortable} property.
35280      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35281      */
35282     /**
35283      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35284      */
35285     /**
35286      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35287      */
35288     /**
35289      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35290      */
35291     /**
35292      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35293      */
35294     /**
35295      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35296      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35297      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35298      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35299      */
35300        /**
35301      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35302      */
35303     /**
35304      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35305      */
35306     /**
35307      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35308      */
35309     /**
35310      * @cfg {String} cursor (Optional)
35311      */
35312     /**
35313      * @cfg {String} tooltip (Optional)
35314      */
35315     /**
35316      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35317      */
35318     /**
35319      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35320      */
35321     /**
35322      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35323      */
35324     /**
35325      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35326      */
35327         /**
35328      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35329      */
35330     /**
35331      * Returns the id of the column at the specified index.
35332      * @param {Number} index The column index
35333      * @return {String} the id
35334      */
35335     getColumnId : function(index){
35336         return this.config[index].id;
35337     },
35338
35339     /**
35340      * Returns the column for a specified id.
35341      * @param {String} id The column id
35342      * @return {Object} the column
35343      */
35344     getColumnById : function(id){
35345         return this.lookup[id];
35346     },
35347
35348     
35349     /**
35350      * Returns the column Object for a specified dataIndex.
35351      * @param {String} dataIndex The column dataIndex
35352      * @return {Object|Boolean} the column or false if not found
35353      */
35354     getColumnByDataIndex: function(dataIndex){
35355         var index = this.findColumnIndex(dataIndex);
35356         return index > -1 ? this.config[index] : false;
35357     },
35358     
35359     /**
35360      * Returns the index for a specified column id.
35361      * @param {String} id The column id
35362      * @return {Number} the index, or -1 if not found
35363      */
35364     getIndexById : function(id){
35365         for(var i = 0, len = this.config.length; i < len; i++){
35366             if(this.config[i].id == id){
35367                 return i;
35368             }
35369         }
35370         return -1;
35371     },
35372     
35373     /**
35374      * Returns the index for a specified column dataIndex.
35375      * @param {String} dataIndex The column dataIndex
35376      * @return {Number} the index, or -1 if not found
35377      */
35378     
35379     findColumnIndex : function(dataIndex){
35380         for(var i = 0, len = this.config.length; i < len; i++){
35381             if(this.config[i].dataIndex == dataIndex){
35382                 return i;
35383             }
35384         }
35385         return -1;
35386     },
35387     
35388     
35389     moveColumn : function(oldIndex, newIndex){
35390         var c = this.config[oldIndex];
35391         this.config.splice(oldIndex, 1);
35392         this.config.splice(newIndex, 0, c);
35393         this.dataMap = null;
35394         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35395     },
35396
35397     isLocked : function(colIndex){
35398         return this.config[colIndex].locked === true;
35399     },
35400
35401     setLocked : function(colIndex, value, suppressEvent){
35402         if(this.isLocked(colIndex) == value){
35403             return;
35404         }
35405         this.config[colIndex].locked = value;
35406         if(!suppressEvent){
35407             this.fireEvent("columnlockchange", this, colIndex, value);
35408         }
35409     },
35410
35411     getTotalLockedWidth : function(){
35412         var totalWidth = 0;
35413         for(var i = 0; i < this.config.length; i++){
35414             if(this.isLocked(i) && !this.isHidden(i)){
35415                 this.totalWidth += this.getColumnWidth(i);
35416             }
35417         }
35418         return totalWidth;
35419     },
35420
35421     getLockedCount : function(){
35422         for(var i = 0, len = this.config.length; i < len; i++){
35423             if(!this.isLocked(i)){
35424                 return i;
35425             }
35426         }
35427         
35428         return this.config.length;
35429     },
35430
35431     /**
35432      * Returns the number of columns.
35433      * @return {Number}
35434      */
35435     getColumnCount : function(visibleOnly){
35436         if(visibleOnly === true){
35437             var c = 0;
35438             for(var i = 0, len = this.config.length; i < len; i++){
35439                 if(!this.isHidden(i)){
35440                     c++;
35441                 }
35442             }
35443             return c;
35444         }
35445         return this.config.length;
35446     },
35447
35448     /**
35449      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35450      * @param {Function} fn
35451      * @param {Object} scope (optional)
35452      * @return {Array} result
35453      */
35454     getColumnsBy : function(fn, scope){
35455         var r = [];
35456         for(var i = 0, len = this.config.length; i < len; i++){
35457             var c = this.config[i];
35458             if(fn.call(scope||this, c, i) === true){
35459                 r[r.length] = c;
35460             }
35461         }
35462         return r;
35463     },
35464
35465     /**
35466      * Returns true if the specified column is sortable.
35467      * @param {Number} col The column index
35468      * @return {Boolean}
35469      */
35470     isSortable : function(col){
35471         if(typeof this.config[col].sortable == "undefined"){
35472             return this.defaultSortable;
35473         }
35474         return this.config[col].sortable;
35475     },
35476
35477     /**
35478      * Returns the rendering (formatting) function defined for the column.
35479      * @param {Number} col The column index.
35480      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35481      */
35482     getRenderer : function(col){
35483         if(!this.config[col].renderer){
35484             return Roo.grid.ColumnModel.defaultRenderer;
35485         }
35486         return this.config[col].renderer;
35487     },
35488
35489     /**
35490      * Sets the rendering (formatting) function for a column.
35491      * @param {Number} col The column index
35492      * @param {Function} fn The function to use to process the cell's raw data
35493      * to return HTML markup for the grid view. The render function is called with
35494      * the following parameters:<ul>
35495      * <li>Data value.</li>
35496      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35497      * <li>css A CSS style string to apply to the table cell.</li>
35498      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35499      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35500      * <li>Row index</li>
35501      * <li>Column index</li>
35502      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35503      */
35504     setRenderer : function(col, fn){
35505         this.config[col].renderer = fn;
35506     },
35507
35508     /**
35509      * Returns the width for the specified column.
35510      * @param {Number} col The column index
35511      * @param (optional) {String} gridSize bootstrap width size.
35512      * @return {Number}
35513      */
35514     getColumnWidth : function(col, gridSize)
35515         {
35516                 var cfg = this.config[col];
35517                 
35518                 if (typeof(gridSize) == 'undefined') {
35519                         return cfg.width * 1 || this.defaultWidth;
35520                 }
35521                 if (gridSize === false) { // if we set it..
35522                         return cfg.width || false;
35523                 }
35524                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35525                 
35526                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35527                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35528                                 continue;
35529                         }
35530                         return cfg[ sizes[i] ];
35531                 }
35532                 return 1;
35533                 
35534     },
35535
35536     /**
35537      * Sets the width for a column.
35538      * @param {Number} col The column index
35539      * @param {Number} width The new width
35540      */
35541     setColumnWidth : function(col, width, suppressEvent){
35542         this.config[col].width = width;
35543         this.totalWidth = null;
35544         if(!suppressEvent){
35545              this.fireEvent("widthchange", this, col, width);
35546         }
35547     },
35548
35549     /**
35550      * Returns the total width of all columns.
35551      * @param {Boolean} includeHidden True to include hidden column widths
35552      * @return {Number}
35553      */
35554     getTotalWidth : function(includeHidden){
35555         if(!this.totalWidth){
35556             this.totalWidth = 0;
35557             for(var i = 0, len = this.config.length; i < len; i++){
35558                 if(includeHidden || !this.isHidden(i)){
35559                     this.totalWidth += this.getColumnWidth(i);
35560                 }
35561             }
35562         }
35563         return this.totalWidth;
35564     },
35565
35566     /**
35567      * Returns the header for the specified column.
35568      * @param {Number} col The column index
35569      * @return {String}
35570      */
35571     getColumnHeader : function(col){
35572         return this.config[col].header;
35573     },
35574
35575     /**
35576      * Sets the header for a column.
35577      * @param {Number} col The column index
35578      * @param {String} header The new header
35579      */
35580     setColumnHeader : function(col, header){
35581         this.config[col].header = header;
35582         this.fireEvent("headerchange", this, col, header);
35583     },
35584
35585     /**
35586      * Returns the tooltip for the specified column.
35587      * @param {Number} col The column index
35588      * @return {String}
35589      */
35590     getColumnTooltip : function(col){
35591             return this.config[col].tooltip;
35592     },
35593     /**
35594      * Sets the tooltip for a column.
35595      * @param {Number} col The column index
35596      * @param {String} tooltip The new tooltip
35597      */
35598     setColumnTooltip : function(col, tooltip){
35599             this.config[col].tooltip = tooltip;
35600     },
35601
35602     /**
35603      * Returns the dataIndex for the specified column.
35604      * @param {Number} col The column index
35605      * @return {Number}
35606      */
35607     getDataIndex : function(col){
35608         return this.config[col].dataIndex;
35609     },
35610
35611     /**
35612      * Sets the dataIndex for a column.
35613      * @param {Number} col The column index
35614      * @param {Number} dataIndex The new dataIndex
35615      */
35616     setDataIndex : function(col, dataIndex){
35617         this.config[col].dataIndex = dataIndex;
35618     },
35619
35620     
35621     
35622     /**
35623      * Returns true if the cell is editable.
35624      * @param {Number} colIndex The column index
35625      * @param {Number} rowIndex The row index - this is nto actually used..?
35626      * @return {Boolean}
35627      */
35628     isCellEditable : function(colIndex, rowIndex){
35629         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35630     },
35631
35632     /**
35633      * Returns the editor defined for the cell/column.
35634      * return false or null to disable editing.
35635      * @param {Number} colIndex The column index
35636      * @param {Number} rowIndex The row index
35637      * @return {Object}
35638      */
35639     getCellEditor : function(colIndex, rowIndex){
35640         return this.config[colIndex].editor;
35641     },
35642
35643     /**
35644      * Sets if a column is editable.
35645      * @param {Number} col The column index
35646      * @param {Boolean} editable True if the column is editable
35647      */
35648     setEditable : function(col, editable){
35649         this.config[col].editable = editable;
35650     },
35651
35652
35653     /**
35654      * Returns true if the column is hidden.
35655      * @param {Number} colIndex The column index
35656      * @return {Boolean}
35657      */
35658     isHidden : function(colIndex){
35659         return this.config[colIndex].hidden;
35660     },
35661
35662
35663     /**
35664      * Returns true if the column width cannot be changed
35665      */
35666     isFixed : function(colIndex){
35667         return this.config[colIndex].fixed;
35668     },
35669
35670     /**
35671      * Returns true if the column can be resized
35672      * @return {Boolean}
35673      */
35674     isResizable : function(colIndex){
35675         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35676     },
35677     /**
35678      * Sets if a column is hidden.
35679      * @param {Number} colIndex The column index
35680      * @param {Boolean} hidden True if the column is hidden
35681      */
35682     setHidden : function(colIndex, hidden){
35683         this.config[colIndex].hidden = hidden;
35684         this.totalWidth = null;
35685         this.fireEvent("hiddenchange", this, colIndex, hidden);
35686     },
35687
35688     /**
35689      * Sets the editor for a column.
35690      * @param {Number} col The column index
35691      * @param {Object} editor The editor object
35692      */
35693     setEditor : function(col, editor){
35694         this.config[col].editor = editor;
35695     },
35696     /**
35697      * Add a column (experimental...) - defaults to adding to the end..
35698      * @param {Object} config 
35699     */
35700     addColumn : function(c)
35701     {
35702     
35703         var i = this.config.length;
35704         this.config[i] = c;
35705         
35706         if(typeof c.dataIndex == "undefined"){
35707             c.dataIndex = i;
35708         }
35709         if(typeof c.renderer == "string"){
35710             c.renderer = Roo.util.Format[c.renderer];
35711         }
35712         if(typeof c.id == "undefined"){
35713             c.id = Roo.id();
35714         }
35715         if(c.editor && c.editor.xtype){
35716             c.editor  = Roo.factory(c.editor, Roo.grid);
35717         }
35718         if(c.editor && c.editor.isFormField){
35719             c.editor = new Roo.grid.GridEditor(c.editor);
35720         }
35721         this.lookup[c.id] = c;
35722     }
35723     
35724 });
35725
35726 Roo.grid.ColumnModel.defaultRenderer = function(value)
35727 {
35728     if(typeof value == "object") {
35729         return value;
35730     }
35731         if(typeof value == "string" && value.length < 1){
35732             return "&#160;";
35733         }
35734     
35735         return String.format("{0}", value);
35736 };
35737
35738 // Alias for backwards compatibility
35739 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35740 /*
35741  * Based on:
35742  * Ext JS Library 1.1.1
35743  * Copyright(c) 2006-2007, Ext JS, LLC.
35744  *
35745  * Originally Released Under LGPL - original licence link has changed is not relivant.
35746  *
35747  * Fork - LGPL
35748  * <script type="text/javascript">
35749  */
35750
35751 /**
35752  * @class Roo.grid.AbstractSelectionModel
35753  * @extends Roo.util.Observable
35754  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35755  * implemented by descendant classes.  This class should not be directly instantiated.
35756  * @constructor
35757  */
35758 Roo.grid.AbstractSelectionModel = function(){
35759     this.locked = false;
35760     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35761 };
35762
35763 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35764     /** @ignore Called by the grid automatically. Do not call directly. */
35765     init : function(grid){
35766         this.grid = grid;
35767         this.initEvents();
35768     },
35769
35770     /**
35771      * Locks the selections.
35772      */
35773     lock : function(){
35774         this.locked = true;
35775     },
35776
35777     /**
35778      * Unlocks the selections.
35779      */
35780     unlock : function(){
35781         this.locked = false;
35782     },
35783
35784     /**
35785      * Returns true if the selections are locked.
35786      * @return {Boolean}
35787      */
35788     isLocked : function(){
35789         return this.locked;
35790     }
35791 });/*
35792  * Based on:
35793  * Ext JS Library 1.1.1
35794  * Copyright(c) 2006-2007, Ext JS, LLC.
35795  *
35796  * Originally Released Under LGPL - original licence link has changed is not relivant.
35797  *
35798  * Fork - LGPL
35799  * <script type="text/javascript">
35800  */
35801 /**
35802  * @extends Roo.grid.AbstractSelectionModel
35803  * @class Roo.grid.RowSelectionModel
35804  * The default SelectionModel used by {@link Roo.grid.Grid}.
35805  * It supports multiple selections and keyboard selection/navigation. 
35806  * @constructor
35807  * @param {Object} config
35808  */
35809 Roo.grid.RowSelectionModel = function(config){
35810     Roo.apply(this, config);
35811     this.selections = new Roo.util.MixedCollection(false, function(o){
35812         return o.id;
35813     });
35814
35815     this.last = false;
35816     this.lastActive = false;
35817
35818     this.addEvents({
35819         /**
35820         * @event selectionchange
35821         * Fires when the selection changes
35822         * @param {SelectionModel} this
35823         */
35824        "selectionchange" : true,
35825        /**
35826         * @event afterselectionchange
35827         * Fires after the selection changes (eg. by key press or clicking)
35828         * @param {SelectionModel} this
35829         */
35830        "afterselectionchange" : true,
35831        /**
35832         * @event beforerowselect
35833         * Fires when a row is selected being selected, return false to cancel.
35834         * @param {SelectionModel} this
35835         * @param {Number} rowIndex The selected index
35836         * @param {Boolean} keepExisting False if other selections will be cleared
35837         */
35838        "beforerowselect" : true,
35839        /**
35840         * @event rowselect
35841         * Fires when a row is selected.
35842         * @param {SelectionModel} this
35843         * @param {Number} rowIndex The selected index
35844         * @param {Roo.data.Record} r The record
35845         */
35846        "rowselect" : true,
35847        /**
35848         * @event rowdeselect
35849         * Fires when a row is deselected.
35850         * @param {SelectionModel} this
35851         * @param {Number} rowIndex The selected index
35852         */
35853         "rowdeselect" : true
35854     });
35855     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35856     this.locked = false;
35857 };
35858
35859 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35860     /**
35861      * @cfg {Boolean} singleSelect
35862      * True to allow selection of only one row at a time (defaults to false)
35863      */
35864     singleSelect : false,
35865
35866     // private
35867     initEvents : function(){
35868
35869         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35870             this.grid.on("mousedown", this.handleMouseDown, this);
35871         }else{ // allow click to work like normal
35872             this.grid.on("rowclick", this.handleDragableRowClick, this);
35873         }
35874         // bootstrap does not have a view..
35875         var view = this.grid.view ? this.grid.view : this.grid;
35876         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35877             "up" : function(e){
35878                 if(!e.shiftKey){
35879                     this.selectPrevious(e.shiftKey);
35880                 }else if(this.last !== false && this.lastActive !== false){
35881                     var last = this.last;
35882                     this.selectRange(this.last,  this.lastActive-1);
35883                     view.focusRow(this.lastActive);
35884                     if(last !== false){
35885                         this.last = last;
35886                     }
35887                 }else{
35888                     this.selectFirstRow();
35889                 }
35890                 this.fireEvent("afterselectionchange", this);
35891             },
35892             "down" : function(e){
35893                 if(!e.shiftKey){
35894                     this.selectNext(e.shiftKey);
35895                 }else if(this.last !== false && this.lastActive !== false){
35896                     var last = this.last;
35897                     this.selectRange(this.last,  this.lastActive+1);
35898                     view.focusRow(this.lastActive);
35899                     if(last !== false){
35900                         this.last = last;
35901                     }
35902                 }else{
35903                     this.selectFirstRow();
35904                 }
35905                 this.fireEvent("afterselectionchange", this);
35906             },
35907             scope: this
35908         });
35909
35910          
35911         view.on("refresh", this.onRefresh, this);
35912         view.on("rowupdated", this.onRowUpdated, this);
35913         view.on("rowremoved", this.onRemove, this);
35914     },
35915
35916     // private
35917     onRefresh : function(){
35918         var ds = this.grid.ds, i, v = this.grid.view;
35919         var s = this.selections;
35920         s.each(function(r){
35921             if((i = ds.indexOfId(r.id)) != -1){
35922                 v.onRowSelect(i);
35923                 s.add(ds.getAt(i)); // updating the selection relate data
35924             }else{
35925                 s.remove(r);
35926             }
35927         });
35928     },
35929
35930     // private
35931     onRemove : function(v, index, r){
35932         this.selections.remove(r);
35933     },
35934
35935     // private
35936     onRowUpdated : function(v, index, r){
35937         if(this.isSelected(r)){
35938             v.onRowSelect(index);
35939         }
35940     },
35941
35942     /**
35943      * Select records.
35944      * @param {Array} records The records to select
35945      * @param {Boolean} keepExisting (optional) True to keep existing selections
35946      */
35947     selectRecords : function(records, keepExisting){
35948         if(!keepExisting){
35949             this.clearSelections();
35950         }
35951         var ds = this.grid.ds;
35952         for(var i = 0, len = records.length; i < len; i++){
35953             this.selectRow(ds.indexOf(records[i]), true);
35954         }
35955     },
35956
35957     /**
35958      * Gets the number of selected rows.
35959      * @return {Number}
35960      */
35961     getCount : function(){
35962         return this.selections.length;
35963     },
35964
35965     /**
35966      * Selects the first row in the grid.
35967      */
35968     selectFirstRow : function(){
35969         this.selectRow(0);
35970     },
35971
35972     /**
35973      * Select the last row.
35974      * @param {Boolean} keepExisting (optional) True to keep existing selections
35975      */
35976     selectLastRow : function(keepExisting){
35977         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
35978     },
35979
35980     /**
35981      * Selects the row immediately following the last selected row.
35982      * @param {Boolean} keepExisting (optional) True to keep existing selections
35983      */
35984     selectNext : function(keepExisting){
35985         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
35986             this.selectRow(this.last+1, keepExisting);
35987             var view = this.grid.view ? this.grid.view : this.grid;
35988             view.focusRow(this.last);
35989         }
35990     },
35991
35992     /**
35993      * Selects the row that precedes the last selected row.
35994      * @param {Boolean} keepExisting (optional) True to keep existing selections
35995      */
35996     selectPrevious : function(keepExisting){
35997         if(this.last){
35998             this.selectRow(this.last-1, keepExisting);
35999             var view = this.grid.view ? this.grid.view : this.grid;
36000             view.focusRow(this.last);
36001         }
36002     },
36003
36004     /**
36005      * Returns the selected records
36006      * @return {Array} Array of selected records
36007      */
36008     getSelections : function(){
36009         return [].concat(this.selections.items);
36010     },
36011
36012     /**
36013      * Returns the first selected record.
36014      * @return {Record}
36015      */
36016     getSelected : function(){
36017         return this.selections.itemAt(0);
36018     },
36019
36020
36021     /**
36022      * Clears all selections.
36023      */
36024     clearSelections : function(fast){
36025         if(this.locked) {
36026             return;
36027         }
36028         if(fast !== true){
36029             var ds = this.grid.ds;
36030             var s = this.selections;
36031             s.each(function(r){
36032                 this.deselectRow(ds.indexOfId(r.id));
36033             }, this);
36034             s.clear();
36035         }else{
36036             this.selections.clear();
36037         }
36038         this.last = false;
36039     },
36040
36041
36042     /**
36043      * Selects all rows.
36044      */
36045     selectAll : function(){
36046         if(this.locked) {
36047             return;
36048         }
36049         this.selections.clear();
36050         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36051             this.selectRow(i, true);
36052         }
36053     },
36054
36055     /**
36056      * Returns True if there is a selection.
36057      * @return {Boolean}
36058      */
36059     hasSelection : function(){
36060         return this.selections.length > 0;
36061     },
36062
36063     /**
36064      * Returns True if the specified row is selected.
36065      * @param {Number/Record} record The record or index of the record to check
36066      * @return {Boolean}
36067      */
36068     isSelected : function(index){
36069         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36070         return (r && this.selections.key(r.id) ? true : false);
36071     },
36072
36073     /**
36074      * Returns True if the specified record id is selected.
36075      * @param {String} id The id of record to check
36076      * @return {Boolean}
36077      */
36078     isIdSelected : function(id){
36079         return (this.selections.key(id) ? true : false);
36080     },
36081
36082     // private
36083     handleMouseDown : function(e, t)
36084     {
36085         var view = this.grid.view ? this.grid.view : this.grid;
36086         var rowIndex;
36087         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36088             return;
36089         };
36090         if(e.shiftKey && this.last !== false){
36091             var last = this.last;
36092             this.selectRange(last, rowIndex, e.ctrlKey);
36093             this.last = last; // reset the last
36094             view.focusRow(rowIndex);
36095         }else{
36096             var isSelected = this.isSelected(rowIndex);
36097             if(e.button !== 0 && isSelected){
36098                 view.focusRow(rowIndex);
36099             }else if(e.ctrlKey && isSelected){
36100                 this.deselectRow(rowIndex);
36101             }else if(!isSelected){
36102                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36103                 view.focusRow(rowIndex);
36104             }
36105         }
36106         this.fireEvent("afterselectionchange", this);
36107     },
36108     // private
36109     handleDragableRowClick :  function(grid, rowIndex, e) 
36110     {
36111         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36112             this.selectRow(rowIndex, false);
36113             var view = this.grid.view ? this.grid.view : this.grid;
36114             view.focusRow(rowIndex);
36115              this.fireEvent("afterselectionchange", this);
36116         }
36117     },
36118     
36119     /**
36120      * Selects multiple rows.
36121      * @param {Array} rows Array of the indexes of the row to select
36122      * @param {Boolean} keepExisting (optional) True to keep existing selections
36123      */
36124     selectRows : function(rows, keepExisting){
36125         if(!keepExisting){
36126             this.clearSelections();
36127         }
36128         for(var i = 0, len = rows.length; i < len; i++){
36129             this.selectRow(rows[i], true);
36130         }
36131     },
36132
36133     /**
36134      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36135      * @param {Number} startRow The index of the first row in the range
36136      * @param {Number} endRow The index of the last row in the range
36137      * @param {Boolean} keepExisting (optional) True to retain existing selections
36138      */
36139     selectRange : function(startRow, endRow, keepExisting){
36140         if(this.locked) {
36141             return;
36142         }
36143         if(!keepExisting){
36144             this.clearSelections();
36145         }
36146         if(startRow <= endRow){
36147             for(var i = startRow; i <= endRow; i++){
36148                 this.selectRow(i, true);
36149             }
36150         }else{
36151             for(var i = startRow; i >= endRow; i--){
36152                 this.selectRow(i, true);
36153             }
36154         }
36155     },
36156
36157     /**
36158      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36159      * @param {Number} startRow The index of the first row in the range
36160      * @param {Number} endRow The index of the last row in the range
36161      */
36162     deselectRange : function(startRow, endRow, preventViewNotify){
36163         if(this.locked) {
36164             return;
36165         }
36166         for(var i = startRow; i <= endRow; i++){
36167             this.deselectRow(i, preventViewNotify);
36168         }
36169     },
36170
36171     /**
36172      * Selects a row.
36173      * @param {Number} row The index of the row to select
36174      * @param {Boolean} keepExisting (optional) True to keep existing selections
36175      */
36176     selectRow : function(index, keepExisting, preventViewNotify){
36177         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36178             return;
36179         }
36180         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36181             if(!keepExisting || this.singleSelect){
36182                 this.clearSelections();
36183             }
36184             var r = this.grid.ds.getAt(index);
36185             this.selections.add(r);
36186             this.last = this.lastActive = index;
36187             if(!preventViewNotify){
36188                 var view = this.grid.view ? this.grid.view : this.grid;
36189                 view.onRowSelect(index);
36190             }
36191             this.fireEvent("rowselect", this, index, r);
36192             this.fireEvent("selectionchange", this);
36193         }
36194     },
36195
36196     /**
36197      * Deselects a row.
36198      * @param {Number} row The index of the row to deselect
36199      */
36200     deselectRow : function(index, preventViewNotify){
36201         if(this.locked) {
36202             return;
36203         }
36204         if(this.last == index){
36205             this.last = false;
36206         }
36207         if(this.lastActive == index){
36208             this.lastActive = false;
36209         }
36210         var r = this.grid.ds.getAt(index);
36211         this.selections.remove(r);
36212         if(!preventViewNotify){
36213             var view = this.grid.view ? this.grid.view : this.grid;
36214             view.onRowDeselect(index);
36215         }
36216         this.fireEvent("rowdeselect", this, index);
36217         this.fireEvent("selectionchange", this);
36218     },
36219
36220     // private
36221     restoreLast : function(){
36222         if(this._last){
36223             this.last = this._last;
36224         }
36225     },
36226
36227     // private
36228     acceptsNav : function(row, col, cm){
36229         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36230     },
36231
36232     // private
36233     onEditorKey : function(field, e){
36234         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36235         if(k == e.TAB){
36236             e.stopEvent();
36237             ed.completeEdit();
36238             if(e.shiftKey){
36239                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36240             }else{
36241                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36242             }
36243         }else if(k == e.ENTER && !e.ctrlKey){
36244             e.stopEvent();
36245             ed.completeEdit();
36246             if(e.shiftKey){
36247                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36248             }else{
36249                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36250             }
36251         }else if(k == e.ESC){
36252             ed.cancelEdit();
36253         }
36254         if(newCell){
36255             g.startEditing(newCell[0], newCell[1]);
36256         }
36257     }
36258 });/*
36259  * Based on:
36260  * Ext JS Library 1.1.1
36261  * Copyright(c) 2006-2007, Ext JS, LLC.
36262  *
36263  * Originally Released Under LGPL - original licence link has changed is not relivant.
36264  *
36265  * Fork - LGPL
36266  * <script type="text/javascript">
36267  */
36268 /**
36269  * @class Roo.grid.CellSelectionModel
36270  * @extends Roo.grid.AbstractSelectionModel
36271  * This class provides the basic implementation for cell selection in a grid.
36272  * @constructor
36273  * @param {Object} config The object containing the configuration of this model.
36274  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36275  */
36276 Roo.grid.CellSelectionModel = function(config){
36277     Roo.apply(this, config);
36278
36279     this.selection = null;
36280
36281     this.addEvents({
36282         /**
36283              * @event beforerowselect
36284              * Fires before a cell is selected.
36285              * @param {SelectionModel} this
36286              * @param {Number} rowIndex The selected row index
36287              * @param {Number} colIndex The selected cell index
36288              */
36289             "beforecellselect" : true,
36290         /**
36291              * @event cellselect
36292              * Fires when a cell is selected.
36293              * @param {SelectionModel} this
36294              * @param {Number} rowIndex The selected row index
36295              * @param {Number} colIndex The selected cell index
36296              */
36297             "cellselect" : true,
36298         /**
36299              * @event selectionchange
36300              * Fires when the active selection changes.
36301              * @param {SelectionModel} this
36302              * @param {Object} selection null for no selection or an object (o) with two properties
36303                 <ul>
36304                 <li>o.record: the record object for the row the selection is in</li>
36305                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36306                 </ul>
36307              */
36308             "selectionchange" : true,
36309         /**
36310              * @event tabend
36311              * Fires when the tab (or enter) was pressed on the last editable cell
36312              * You can use this to trigger add new row.
36313              * @param {SelectionModel} this
36314              */
36315             "tabend" : true,
36316          /**
36317              * @event beforeeditnext
36318              * Fires before the next editable sell is made active
36319              * You can use this to skip to another cell or fire the tabend
36320              *    if you set cell to false
36321              * @param {Object} eventdata object : { cell : [ row, col ] } 
36322              */
36323             "beforeeditnext" : true
36324     });
36325     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36326 };
36327
36328 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36329     
36330     enter_is_tab: false,
36331
36332     /** @ignore */
36333     initEvents : function(){
36334         this.grid.on("mousedown", this.handleMouseDown, this);
36335         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36336         var view = this.grid.view;
36337         view.on("refresh", this.onViewChange, this);
36338         view.on("rowupdated", this.onRowUpdated, this);
36339         view.on("beforerowremoved", this.clearSelections, this);
36340         view.on("beforerowsinserted", this.clearSelections, this);
36341         if(this.grid.isEditor){
36342             this.grid.on("beforeedit", this.beforeEdit,  this);
36343         }
36344     },
36345
36346         //private
36347     beforeEdit : function(e){
36348         this.select(e.row, e.column, false, true, e.record);
36349     },
36350
36351         //private
36352     onRowUpdated : function(v, index, r){
36353         if(this.selection && this.selection.record == r){
36354             v.onCellSelect(index, this.selection.cell[1]);
36355         }
36356     },
36357
36358         //private
36359     onViewChange : function(){
36360         this.clearSelections(true);
36361     },
36362
36363         /**
36364          * Returns the currently selected cell,.
36365          * @return {Array} The selected cell (row, column) or null if none selected.
36366          */
36367     getSelectedCell : function(){
36368         return this.selection ? this.selection.cell : null;
36369     },
36370
36371     /**
36372      * Clears all selections.
36373      * @param {Boolean} true to prevent the gridview from being notified about the change.
36374      */
36375     clearSelections : function(preventNotify){
36376         var s = this.selection;
36377         if(s){
36378             if(preventNotify !== true){
36379                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36380             }
36381             this.selection = null;
36382             this.fireEvent("selectionchange", this, null);
36383         }
36384     },
36385
36386     /**
36387      * Returns true if there is a selection.
36388      * @return {Boolean}
36389      */
36390     hasSelection : function(){
36391         return this.selection ? true : false;
36392     },
36393
36394     /** @ignore */
36395     handleMouseDown : function(e, t){
36396         var v = this.grid.getView();
36397         if(this.isLocked()){
36398             return;
36399         };
36400         var row = v.findRowIndex(t);
36401         var cell = v.findCellIndex(t);
36402         if(row !== false && cell !== false){
36403             this.select(row, cell);
36404         }
36405     },
36406
36407     /**
36408      * Selects a cell.
36409      * @param {Number} rowIndex
36410      * @param {Number} collIndex
36411      */
36412     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36413         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36414             this.clearSelections();
36415             r = r || this.grid.dataSource.getAt(rowIndex);
36416             this.selection = {
36417                 record : r,
36418                 cell : [rowIndex, colIndex]
36419             };
36420             if(!preventViewNotify){
36421                 var v = this.grid.getView();
36422                 v.onCellSelect(rowIndex, colIndex);
36423                 if(preventFocus !== true){
36424                     v.focusCell(rowIndex, colIndex);
36425                 }
36426             }
36427             this.fireEvent("cellselect", this, rowIndex, colIndex);
36428             this.fireEvent("selectionchange", this, this.selection);
36429         }
36430     },
36431
36432         //private
36433     isSelectable : function(rowIndex, colIndex, cm){
36434         return !cm.isHidden(colIndex);
36435     },
36436
36437     /** @ignore */
36438     handleKeyDown : function(e){
36439         //Roo.log('Cell Sel Model handleKeyDown');
36440         if(!e.isNavKeyPress()){
36441             return;
36442         }
36443         var g = this.grid, s = this.selection;
36444         if(!s){
36445             e.stopEvent();
36446             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36447             if(cell){
36448                 this.select(cell[0], cell[1]);
36449             }
36450             return;
36451         }
36452         var sm = this;
36453         var walk = function(row, col, step){
36454             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36455         };
36456         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36457         var newCell;
36458
36459       
36460
36461         switch(k){
36462             case e.TAB:
36463                 // handled by onEditorKey
36464                 if (g.isEditor && g.editing) {
36465                     return;
36466                 }
36467                 if(e.shiftKey) {
36468                     newCell = walk(r, c-1, -1);
36469                 } else {
36470                     newCell = walk(r, c+1, 1);
36471                 }
36472                 break;
36473             
36474             case e.DOWN:
36475                newCell = walk(r+1, c, 1);
36476                 break;
36477             
36478             case e.UP:
36479                 newCell = walk(r-1, c, -1);
36480                 break;
36481             
36482             case e.RIGHT:
36483                 newCell = walk(r, c+1, 1);
36484                 break;
36485             
36486             case e.LEFT:
36487                 newCell = walk(r, c-1, -1);
36488                 break;
36489             
36490             case e.ENTER:
36491                 
36492                 if(g.isEditor && !g.editing){
36493                    g.startEditing(r, c);
36494                    e.stopEvent();
36495                    return;
36496                 }
36497                 
36498                 
36499              break;
36500         };
36501         if(newCell){
36502             this.select(newCell[0], newCell[1]);
36503             e.stopEvent();
36504             
36505         }
36506     },
36507
36508     acceptsNav : function(row, col, cm){
36509         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36510     },
36511     /**
36512      * Selects a cell.
36513      * @param {Number} field (not used) - as it's normally used as a listener
36514      * @param {Number} e - event - fake it by using
36515      *
36516      * var e = Roo.EventObjectImpl.prototype;
36517      * e.keyCode = e.TAB
36518      *
36519      * 
36520      */
36521     onEditorKey : function(field, e){
36522         
36523         var k = e.getKey(),
36524             newCell,
36525             g = this.grid,
36526             ed = g.activeEditor,
36527             forward = false;
36528         ///Roo.log('onEditorKey' + k);
36529         
36530         
36531         if (this.enter_is_tab && k == e.ENTER) {
36532             k = e.TAB;
36533         }
36534         
36535         if(k == e.TAB){
36536             if(e.shiftKey){
36537                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36538             }else{
36539                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36540                 forward = true;
36541             }
36542             
36543             e.stopEvent();
36544             
36545         } else if(k == e.ENTER &&  !e.ctrlKey){
36546             ed.completeEdit();
36547             e.stopEvent();
36548             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36549         
36550                 } else if(k == e.ESC){
36551             ed.cancelEdit();
36552         }
36553                 
36554         if (newCell) {
36555             var ecall = { cell : newCell, forward : forward };
36556             this.fireEvent('beforeeditnext', ecall );
36557             newCell = ecall.cell;
36558                         forward = ecall.forward;
36559         }
36560                 
36561         if(newCell){
36562             //Roo.log('next cell after edit');
36563             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36564         } else if (forward) {
36565             // tabbed past last
36566             this.fireEvent.defer(100, this, ['tabend',this]);
36567         }
36568     }
36569 });/*
36570  * Based on:
36571  * Ext JS Library 1.1.1
36572  * Copyright(c) 2006-2007, Ext JS, LLC.
36573  *
36574  * Originally Released Under LGPL - original licence link has changed is not relivant.
36575  *
36576  * Fork - LGPL
36577  * <script type="text/javascript">
36578  */
36579  
36580 /**
36581  * @class Roo.grid.EditorGrid
36582  * @extends Roo.grid.Grid
36583  * Class for creating and editable grid.
36584  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36585  * The container MUST have some type of size defined for the grid to fill. The container will be 
36586  * automatically set to position relative if it isn't already.
36587  * @param {Object} dataSource The data model to bind to
36588  * @param {Object} colModel The column model with info about this grid's columns
36589  */
36590 Roo.grid.EditorGrid = function(container, config){
36591     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36592     this.getGridEl().addClass("xedit-grid");
36593
36594     if(!this.selModel){
36595         this.selModel = new Roo.grid.CellSelectionModel();
36596     }
36597
36598     this.activeEditor = null;
36599
36600         this.addEvents({
36601             /**
36602              * @event beforeedit
36603              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36604              * <ul style="padding:5px;padding-left:16px;">
36605              * <li>grid - This grid</li>
36606              * <li>record - The record being edited</li>
36607              * <li>field - The field name being edited</li>
36608              * <li>value - The value for the field being edited.</li>
36609              * <li>row - The grid row index</li>
36610              * <li>column - The grid column index</li>
36611              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36612              * </ul>
36613              * @param {Object} e An edit event (see above for description)
36614              */
36615             "beforeedit" : true,
36616             /**
36617              * @event afteredit
36618              * Fires after a cell is edited. <br />
36619              * <ul style="padding:5px;padding-left:16px;">
36620              * <li>grid - This grid</li>
36621              * <li>record - The record being edited</li>
36622              * <li>field - The field name being edited</li>
36623              * <li>value - The value being set</li>
36624              * <li>originalValue - The original value for the field, before the edit.</li>
36625              * <li>row - The grid row index</li>
36626              * <li>column - The grid column index</li>
36627              * </ul>
36628              * @param {Object} e An edit event (see above for description)
36629              */
36630             "afteredit" : true,
36631             /**
36632              * @event validateedit
36633              * Fires after a cell is edited, but before the value is set in the record. 
36634          * You can use this to modify the value being set in the field, Return false
36635              * to cancel the change. The edit event object has the following properties <br />
36636              * <ul style="padding:5px;padding-left:16px;">
36637          * <li>editor - This editor</li>
36638              * <li>grid - This grid</li>
36639              * <li>record - The record being edited</li>
36640              * <li>field - The field name being edited</li>
36641              * <li>value - The value being set</li>
36642              * <li>originalValue - The original value for the field, before the edit.</li>
36643              * <li>row - The grid row index</li>
36644              * <li>column - The grid column index</li>
36645              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36646              * </ul>
36647              * @param {Object} e An edit event (see above for description)
36648              */
36649             "validateedit" : true
36650         });
36651     this.on("bodyscroll", this.stopEditing,  this);
36652     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36653 };
36654
36655 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36656     /**
36657      * @cfg {Number} clicksToEdit
36658      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36659      */
36660     clicksToEdit: 2,
36661
36662     // private
36663     isEditor : true,
36664     // private
36665     trackMouseOver: false, // causes very odd FF errors
36666
36667     onCellDblClick : function(g, row, col){
36668         this.startEditing(row, col);
36669     },
36670
36671     onEditComplete : function(ed, value, startValue){
36672         this.editing = false;
36673         this.activeEditor = null;
36674         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36675         var r = ed.record;
36676         var field = this.colModel.getDataIndex(ed.col);
36677         var e = {
36678             grid: this,
36679             record: r,
36680             field: field,
36681             originalValue: startValue,
36682             value: value,
36683             row: ed.row,
36684             column: ed.col,
36685             cancel:false,
36686             editor: ed
36687         };
36688         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36689         cell.show();
36690           
36691         if(String(value) !== String(startValue)){
36692             
36693             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36694                 r.set(field, e.value);
36695                 // if we are dealing with a combo box..
36696                 // then we also set the 'name' colum to be the displayField
36697                 if (ed.field.displayField && ed.field.name) {
36698                     r.set(ed.field.name, ed.field.el.dom.value);
36699                 }
36700                 
36701                 delete e.cancel; //?? why!!!
36702                 this.fireEvent("afteredit", e);
36703             }
36704         } else {
36705             this.fireEvent("afteredit", e); // always fire it!
36706         }
36707         this.view.focusCell(ed.row, ed.col);
36708     },
36709
36710     /**
36711      * Starts editing the specified for the specified row/column
36712      * @param {Number} rowIndex
36713      * @param {Number} colIndex
36714      */
36715     startEditing : function(row, col){
36716         this.stopEditing();
36717         if(this.colModel.isCellEditable(col, row)){
36718             this.view.ensureVisible(row, col, true);
36719           
36720             var r = this.dataSource.getAt(row);
36721             var field = this.colModel.getDataIndex(col);
36722             var cell = Roo.get(this.view.getCell(row,col));
36723             var e = {
36724                 grid: this,
36725                 record: r,
36726                 field: field,
36727                 value: r.data[field],
36728                 row: row,
36729                 column: col,
36730                 cancel:false 
36731             };
36732             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36733                 this.editing = true;
36734                 var ed = this.colModel.getCellEditor(col, row);
36735                 
36736                 if (!ed) {
36737                     return;
36738                 }
36739                 if(!ed.rendered){
36740                     ed.render(ed.parentEl || document.body);
36741                 }
36742                 ed.field.reset();
36743                
36744                 cell.hide();
36745                 
36746                 (function(){ // complex but required for focus issues in safari, ie and opera
36747                     ed.row = row;
36748                     ed.col = col;
36749                     ed.record = r;
36750                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36751                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36752                     this.activeEditor = ed;
36753                     var v = r.data[field];
36754                     ed.startEdit(this.view.getCell(row, col), v);
36755                     // combo's with 'displayField and name set
36756                     if (ed.field.displayField && ed.field.name) {
36757                         ed.field.el.dom.value = r.data[ed.field.name];
36758                     }
36759                     
36760                     
36761                 }).defer(50, this);
36762             }
36763         }
36764     },
36765         
36766     /**
36767      * Stops any active editing
36768      */
36769     stopEditing : function(){
36770         if(this.activeEditor){
36771             this.activeEditor.completeEdit();
36772         }
36773         this.activeEditor = null;
36774     },
36775         
36776          /**
36777      * Called to get grid's drag proxy text, by default returns this.ddText.
36778      * @return {String}
36779      */
36780     getDragDropText : function(){
36781         var count = this.selModel.getSelectedCell() ? 1 : 0;
36782         return String.format(this.ddText, count, count == 1 ? '' : 's');
36783     }
36784         
36785 });/*
36786  * Based on:
36787  * Ext JS Library 1.1.1
36788  * Copyright(c) 2006-2007, Ext JS, LLC.
36789  *
36790  * Originally Released Under LGPL - original licence link has changed is not relivant.
36791  *
36792  * Fork - LGPL
36793  * <script type="text/javascript">
36794  */
36795
36796 // private - not really -- you end up using it !
36797 // This is a support class used internally by the Grid components
36798
36799 /**
36800  * @class Roo.grid.GridEditor
36801  * @extends Roo.Editor
36802  * Class for creating and editable grid elements.
36803  * @param {Object} config any settings (must include field)
36804  */
36805 Roo.grid.GridEditor = function(field, config){
36806     if (!config && field.field) {
36807         config = field;
36808         field = Roo.factory(config.field, Roo.form);
36809     }
36810     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36811     field.monitorTab = false;
36812 };
36813
36814 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36815     
36816     /**
36817      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36818      */
36819     
36820     alignment: "tl-tl",
36821     autoSize: "width",
36822     hideEl : false,
36823     cls: "x-small-editor x-grid-editor",
36824     shim:false,
36825     shadow:"frame"
36826 });/*
36827  * Based on:
36828  * Ext JS Library 1.1.1
36829  * Copyright(c) 2006-2007, Ext JS, LLC.
36830  *
36831  * Originally Released Under LGPL - original licence link has changed is not relivant.
36832  *
36833  * Fork - LGPL
36834  * <script type="text/javascript">
36835  */
36836   
36837
36838   
36839 Roo.grid.PropertyRecord = Roo.data.Record.create([
36840     {name:'name',type:'string'},  'value'
36841 ]);
36842
36843
36844 Roo.grid.PropertyStore = function(grid, source){
36845     this.grid = grid;
36846     this.store = new Roo.data.Store({
36847         recordType : Roo.grid.PropertyRecord
36848     });
36849     this.store.on('update', this.onUpdate,  this);
36850     if(source){
36851         this.setSource(source);
36852     }
36853     Roo.grid.PropertyStore.superclass.constructor.call(this);
36854 };
36855
36856
36857
36858 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36859     setSource : function(o){
36860         this.source = o;
36861         this.store.removeAll();
36862         var data = [];
36863         for(var k in o){
36864             if(this.isEditableValue(o[k])){
36865                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36866             }
36867         }
36868         this.store.loadRecords({records: data}, {}, true);
36869     },
36870
36871     onUpdate : function(ds, record, type){
36872         if(type == Roo.data.Record.EDIT){
36873             var v = record.data['value'];
36874             var oldValue = record.modified['value'];
36875             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36876                 this.source[record.id] = v;
36877                 record.commit();
36878                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36879             }else{
36880                 record.reject();
36881             }
36882         }
36883     },
36884
36885     getProperty : function(row){
36886        return this.store.getAt(row);
36887     },
36888
36889     isEditableValue: function(val){
36890         if(val && val instanceof Date){
36891             return true;
36892         }else if(typeof val == 'object' || typeof val == 'function'){
36893             return false;
36894         }
36895         return true;
36896     },
36897
36898     setValue : function(prop, value){
36899         this.source[prop] = value;
36900         this.store.getById(prop).set('value', value);
36901     },
36902
36903     getSource : function(){
36904         return this.source;
36905     }
36906 });
36907
36908 Roo.grid.PropertyColumnModel = function(grid, store){
36909     this.grid = grid;
36910     var g = Roo.grid;
36911     g.PropertyColumnModel.superclass.constructor.call(this, [
36912         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36913         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36914     ]);
36915     this.store = store;
36916     this.bselect = Roo.DomHelper.append(document.body, {
36917         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36918             {tag: 'option', value: 'true', html: 'true'},
36919             {tag: 'option', value: 'false', html: 'false'}
36920         ]
36921     });
36922     Roo.id(this.bselect);
36923     var f = Roo.form;
36924     this.editors = {
36925         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36926         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36927         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36928         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36929         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36930     };
36931     this.renderCellDelegate = this.renderCell.createDelegate(this);
36932     this.renderPropDelegate = this.renderProp.createDelegate(this);
36933 };
36934
36935 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36936     
36937     
36938     nameText : 'Name',
36939     valueText : 'Value',
36940     
36941     dateFormat : 'm/j/Y',
36942     
36943     
36944     renderDate : function(dateVal){
36945         return dateVal.dateFormat(this.dateFormat);
36946     },
36947
36948     renderBool : function(bVal){
36949         return bVal ? 'true' : 'false';
36950     },
36951
36952     isCellEditable : function(colIndex, rowIndex){
36953         return colIndex == 1;
36954     },
36955
36956     getRenderer : function(col){
36957         return col == 1 ?
36958             this.renderCellDelegate : this.renderPropDelegate;
36959     },
36960
36961     renderProp : function(v){
36962         return this.getPropertyName(v);
36963     },
36964
36965     renderCell : function(val){
36966         var rv = val;
36967         if(val instanceof Date){
36968             rv = this.renderDate(val);
36969         }else if(typeof val == 'boolean'){
36970             rv = this.renderBool(val);
36971         }
36972         return Roo.util.Format.htmlEncode(rv);
36973     },
36974
36975     getPropertyName : function(name){
36976         var pn = this.grid.propertyNames;
36977         return pn && pn[name] ? pn[name] : name;
36978     },
36979
36980     getCellEditor : function(colIndex, rowIndex){
36981         var p = this.store.getProperty(rowIndex);
36982         var n = p.data['name'], val = p.data['value'];
36983         
36984         if(typeof(this.grid.customEditors[n]) == 'string'){
36985             return this.editors[this.grid.customEditors[n]];
36986         }
36987         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36988             return this.grid.customEditors[n];
36989         }
36990         if(val instanceof Date){
36991             return this.editors['date'];
36992         }else if(typeof val == 'number'){
36993             return this.editors['number'];
36994         }else if(typeof val == 'boolean'){
36995             return this.editors['boolean'];
36996         }else{
36997             return this.editors['string'];
36998         }
36999     }
37000 });
37001
37002 /**
37003  * @class Roo.grid.PropertyGrid
37004  * @extends Roo.grid.EditorGrid
37005  * This class represents the  interface of a component based property grid control.
37006  * <br><br>Usage:<pre><code>
37007  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37008       
37009  });
37010  // set any options
37011  grid.render();
37012  * </code></pre>
37013   
37014  * @constructor
37015  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37016  * The container MUST have some type of size defined for the grid to fill. The container will be
37017  * automatically set to position relative if it isn't already.
37018  * @param {Object} config A config object that sets properties on this grid.
37019  */
37020 Roo.grid.PropertyGrid = function(container, config){
37021     config = config || {};
37022     var store = new Roo.grid.PropertyStore(this);
37023     this.store = store;
37024     var cm = new Roo.grid.PropertyColumnModel(this, store);
37025     store.store.sort('name', 'ASC');
37026     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37027         ds: store.store,
37028         cm: cm,
37029         enableColLock:false,
37030         enableColumnMove:false,
37031         stripeRows:false,
37032         trackMouseOver: false,
37033         clicksToEdit:1
37034     }, config));
37035     this.getGridEl().addClass('x-props-grid');
37036     this.lastEditRow = null;
37037     this.on('columnresize', this.onColumnResize, this);
37038     this.addEvents({
37039          /**
37040              * @event beforepropertychange
37041              * Fires before a property changes (return false to stop?)
37042              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37043              * @param {String} id Record Id
37044              * @param {String} newval New Value
37045          * @param {String} oldval Old Value
37046              */
37047         "beforepropertychange": true,
37048         /**
37049              * @event propertychange
37050              * Fires after a property changes
37051              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37052              * @param {String} id Record Id
37053              * @param {String} newval New Value
37054          * @param {String} oldval Old Value
37055              */
37056         "propertychange": true
37057     });
37058     this.customEditors = this.customEditors || {};
37059 };
37060 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37061     
37062      /**
37063      * @cfg {Object} customEditors map of colnames=> custom editors.
37064      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37065      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37066      * false disables editing of the field.
37067          */
37068     
37069       /**
37070      * @cfg {Object} propertyNames map of property Names to their displayed value
37071          */
37072     
37073     render : function(){
37074         Roo.grid.PropertyGrid.superclass.render.call(this);
37075         this.autoSize.defer(100, this);
37076     },
37077
37078     autoSize : function(){
37079         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37080         if(this.view){
37081             this.view.fitColumns();
37082         }
37083     },
37084
37085     onColumnResize : function(){
37086         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37087         this.autoSize();
37088     },
37089     /**
37090      * Sets the data for the Grid
37091      * accepts a Key => Value object of all the elements avaiable.
37092      * @param {Object} data  to appear in grid.
37093      */
37094     setSource : function(source){
37095         this.store.setSource(source);
37096         //this.autoSize();
37097     },
37098     /**
37099      * Gets all the data from the grid.
37100      * @return {Object} data  data stored in grid
37101      */
37102     getSource : function(){
37103         return this.store.getSource();
37104     }
37105 });/*
37106   
37107  * Licence LGPL
37108  
37109  */
37110  
37111 /**
37112  * @class Roo.grid.Calendar
37113  * @extends Roo.util.Grid
37114  * This class extends the Grid to provide a calendar widget
37115  * <br><br>Usage:<pre><code>
37116  var grid = new Roo.grid.Calendar("my-container-id", {
37117      ds: myDataStore,
37118      cm: myColModel,
37119      selModel: mySelectionModel,
37120      autoSizeColumns: true,
37121      monitorWindowResize: false,
37122      trackMouseOver: true
37123      eventstore : real data store..
37124  });
37125  // set any options
37126  grid.render();
37127   
37128   * @constructor
37129  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37130  * The container MUST have some type of size defined for the grid to fill. The container will be
37131  * automatically set to position relative if it isn't already.
37132  * @param {Object} config A config object that sets properties on this grid.
37133  */
37134 Roo.grid.Calendar = function(container, config){
37135         // initialize the container
37136         this.container = Roo.get(container);
37137         this.container.update("");
37138         this.container.setStyle("overflow", "hidden");
37139     this.container.addClass('x-grid-container');
37140
37141     this.id = this.container.id;
37142
37143     Roo.apply(this, config);
37144     // check and correct shorthanded configs
37145     
37146     var rows = [];
37147     var d =1;
37148     for (var r = 0;r < 6;r++) {
37149         
37150         rows[r]=[];
37151         for (var c =0;c < 7;c++) {
37152             rows[r][c]= '';
37153         }
37154     }
37155     if (this.eventStore) {
37156         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37157         this.eventStore.on('load',this.onLoad, this);
37158         this.eventStore.on('beforeload',this.clearEvents, this);
37159          
37160     }
37161     
37162     this.dataSource = new Roo.data.Store({
37163             proxy: new Roo.data.MemoryProxy(rows),
37164             reader: new Roo.data.ArrayReader({}, [
37165                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37166     });
37167
37168     this.dataSource.load();
37169     this.ds = this.dataSource;
37170     this.ds.xmodule = this.xmodule || false;
37171     
37172     
37173     var cellRender = function(v,x,r)
37174     {
37175         return String.format(
37176             '<div class="fc-day  fc-widget-content"><div>' +
37177                 '<div class="fc-event-container"></div>' +
37178                 '<div class="fc-day-number">{0}</div>'+
37179                 
37180                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37181             '</div></div>', v);
37182     
37183     }
37184     
37185     
37186     this.colModel = new Roo.grid.ColumnModel( [
37187         {
37188             xtype: 'ColumnModel',
37189             xns: Roo.grid,
37190             dataIndex : 'weekday0',
37191             header : 'Sunday',
37192             renderer : cellRender
37193         },
37194         {
37195             xtype: 'ColumnModel',
37196             xns: Roo.grid,
37197             dataIndex : 'weekday1',
37198             header : 'Monday',
37199             renderer : cellRender
37200         },
37201         {
37202             xtype: 'ColumnModel',
37203             xns: Roo.grid,
37204             dataIndex : 'weekday2',
37205             header : 'Tuesday',
37206             renderer : cellRender
37207         },
37208         {
37209             xtype: 'ColumnModel',
37210             xns: Roo.grid,
37211             dataIndex : 'weekday3',
37212             header : 'Wednesday',
37213             renderer : cellRender
37214         },
37215         {
37216             xtype: 'ColumnModel',
37217             xns: Roo.grid,
37218             dataIndex : 'weekday4',
37219             header : 'Thursday',
37220             renderer : cellRender
37221         },
37222         {
37223             xtype: 'ColumnModel',
37224             xns: Roo.grid,
37225             dataIndex : 'weekday5',
37226             header : 'Friday',
37227             renderer : cellRender
37228         },
37229         {
37230             xtype: 'ColumnModel',
37231             xns: Roo.grid,
37232             dataIndex : 'weekday6',
37233             header : 'Saturday',
37234             renderer : cellRender
37235         }
37236     ]);
37237     this.cm = this.colModel;
37238     this.cm.xmodule = this.xmodule || false;
37239  
37240         
37241           
37242     //this.selModel = new Roo.grid.CellSelectionModel();
37243     //this.sm = this.selModel;
37244     //this.selModel.init(this);
37245     
37246     
37247     if(this.width){
37248         this.container.setWidth(this.width);
37249     }
37250
37251     if(this.height){
37252         this.container.setHeight(this.height);
37253     }
37254     /** @private */
37255         this.addEvents({
37256         // raw events
37257         /**
37258          * @event click
37259          * The raw click event for the entire grid.
37260          * @param {Roo.EventObject} e
37261          */
37262         "click" : true,
37263         /**
37264          * @event dblclick
37265          * The raw dblclick event for the entire grid.
37266          * @param {Roo.EventObject} e
37267          */
37268         "dblclick" : true,
37269         /**
37270          * @event contextmenu
37271          * The raw contextmenu event for the entire grid.
37272          * @param {Roo.EventObject} e
37273          */
37274         "contextmenu" : true,
37275         /**
37276          * @event mousedown
37277          * The raw mousedown event for the entire grid.
37278          * @param {Roo.EventObject} e
37279          */
37280         "mousedown" : true,
37281         /**
37282          * @event mouseup
37283          * The raw mouseup event for the entire grid.
37284          * @param {Roo.EventObject} e
37285          */
37286         "mouseup" : true,
37287         /**
37288          * @event mouseover
37289          * The raw mouseover event for the entire grid.
37290          * @param {Roo.EventObject} e
37291          */
37292         "mouseover" : true,
37293         /**
37294          * @event mouseout
37295          * The raw mouseout event for the entire grid.
37296          * @param {Roo.EventObject} e
37297          */
37298         "mouseout" : true,
37299         /**
37300          * @event keypress
37301          * The raw keypress event for the entire grid.
37302          * @param {Roo.EventObject} e
37303          */
37304         "keypress" : true,
37305         /**
37306          * @event keydown
37307          * The raw keydown event for the entire grid.
37308          * @param {Roo.EventObject} e
37309          */
37310         "keydown" : true,
37311
37312         // custom events
37313
37314         /**
37315          * @event cellclick
37316          * Fires when a cell is clicked
37317          * @param {Grid} this
37318          * @param {Number} rowIndex
37319          * @param {Number} columnIndex
37320          * @param {Roo.EventObject} e
37321          */
37322         "cellclick" : true,
37323         /**
37324          * @event celldblclick
37325          * Fires when a cell is double clicked
37326          * @param {Grid} this
37327          * @param {Number} rowIndex
37328          * @param {Number} columnIndex
37329          * @param {Roo.EventObject} e
37330          */
37331         "celldblclick" : true,
37332         /**
37333          * @event rowclick
37334          * Fires when a row is clicked
37335          * @param {Grid} this
37336          * @param {Number} rowIndex
37337          * @param {Roo.EventObject} e
37338          */
37339         "rowclick" : true,
37340         /**
37341          * @event rowdblclick
37342          * Fires when a row is double clicked
37343          * @param {Grid} this
37344          * @param {Number} rowIndex
37345          * @param {Roo.EventObject} e
37346          */
37347         "rowdblclick" : true,
37348         /**
37349          * @event headerclick
37350          * Fires when a header is clicked
37351          * @param {Grid} this
37352          * @param {Number} columnIndex
37353          * @param {Roo.EventObject} e
37354          */
37355         "headerclick" : true,
37356         /**
37357          * @event headerdblclick
37358          * Fires when a header cell is double clicked
37359          * @param {Grid} this
37360          * @param {Number} columnIndex
37361          * @param {Roo.EventObject} e
37362          */
37363         "headerdblclick" : true,
37364         /**
37365          * @event rowcontextmenu
37366          * Fires when a row is right clicked
37367          * @param {Grid} this
37368          * @param {Number} rowIndex
37369          * @param {Roo.EventObject} e
37370          */
37371         "rowcontextmenu" : true,
37372         /**
37373          * @event cellcontextmenu
37374          * Fires when a cell is right clicked
37375          * @param {Grid} this
37376          * @param {Number} rowIndex
37377          * @param {Number} cellIndex
37378          * @param {Roo.EventObject} e
37379          */
37380          "cellcontextmenu" : true,
37381         /**
37382          * @event headercontextmenu
37383          * Fires when a header is right clicked
37384          * @param {Grid} this
37385          * @param {Number} columnIndex
37386          * @param {Roo.EventObject} e
37387          */
37388         "headercontextmenu" : true,
37389         /**
37390          * @event bodyscroll
37391          * Fires when the body element is scrolled
37392          * @param {Number} scrollLeft
37393          * @param {Number} scrollTop
37394          */
37395         "bodyscroll" : true,
37396         /**
37397          * @event columnresize
37398          * Fires when the user resizes a column
37399          * @param {Number} columnIndex
37400          * @param {Number} newSize
37401          */
37402         "columnresize" : true,
37403         /**
37404          * @event columnmove
37405          * Fires when the user moves a column
37406          * @param {Number} oldIndex
37407          * @param {Number} newIndex
37408          */
37409         "columnmove" : true,
37410         /**
37411          * @event startdrag
37412          * Fires when row(s) start being dragged
37413          * @param {Grid} this
37414          * @param {Roo.GridDD} dd The drag drop object
37415          * @param {event} e The raw browser event
37416          */
37417         "startdrag" : true,
37418         /**
37419          * @event enddrag
37420          * Fires when a drag operation is complete
37421          * @param {Grid} this
37422          * @param {Roo.GridDD} dd The drag drop object
37423          * @param {event} e The raw browser event
37424          */
37425         "enddrag" : true,
37426         /**
37427          * @event dragdrop
37428          * Fires when dragged row(s) are dropped on a valid DD target
37429          * @param {Grid} this
37430          * @param {Roo.GridDD} dd The drag drop object
37431          * @param {String} targetId The target drag drop object
37432          * @param {event} e The raw browser event
37433          */
37434         "dragdrop" : true,
37435         /**
37436          * @event dragover
37437          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37438          * @param {Grid} this
37439          * @param {Roo.GridDD} dd The drag drop object
37440          * @param {String} targetId The target drag drop object
37441          * @param {event} e The raw browser event
37442          */
37443         "dragover" : true,
37444         /**
37445          * @event dragenter
37446          *  Fires when the dragged row(s) first cross another DD target while being dragged
37447          * @param {Grid} this
37448          * @param {Roo.GridDD} dd The drag drop object
37449          * @param {String} targetId The target drag drop object
37450          * @param {event} e The raw browser event
37451          */
37452         "dragenter" : true,
37453         /**
37454          * @event dragout
37455          * Fires when the dragged row(s) leave another DD target while being dragged
37456          * @param {Grid} this
37457          * @param {Roo.GridDD} dd The drag drop object
37458          * @param {String} targetId The target drag drop object
37459          * @param {event} e The raw browser event
37460          */
37461         "dragout" : true,
37462         /**
37463          * @event rowclass
37464          * Fires when a row is rendered, so you can change add a style to it.
37465          * @param {GridView} gridview   The grid view
37466          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37467          */
37468         'rowclass' : true,
37469
37470         /**
37471          * @event render
37472          * Fires when the grid is rendered
37473          * @param {Grid} grid
37474          */
37475         'render' : true,
37476             /**
37477              * @event select
37478              * Fires when a date is selected
37479              * @param {DatePicker} this
37480              * @param {Date} date The selected date
37481              */
37482         'select': true,
37483         /**
37484              * @event monthchange
37485              * Fires when the displayed month changes 
37486              * @param {DatePicker} this
37487              * @param {Date} date The selected month
37488              */
37489         'monthchange': true,
37490         /**
37491              * @event evententer
37492              * Fires when mouse over an event
37493              * @param {Calendar} this
37494              * @param {event} Event
37495              */
37496         'evententer': true,
37497         /**
37498              * @event eventleave
37499              * Fires when the mouse leaves an
37500              * @param {Calendar} this
37501              * @param {event}
37502              */
37503         'eventleave': true,
37504         /**
37505              * @event eventclick
37506              * Fires when the mouse click an
37507              * @param {Calendar} this
37508              * @param {event}
37509              */
37510         'eventclick': true,
37511         /**
37512              * @event eventrender
37513              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37514              * @param {Calendar} this
37515              * @param {data} data to be modified
37516              */
37517         'eventrender': true
37518         
37519     });
37520
37521     Roo.grid.Grid.superclass.constructor.call(this);
37522     this.on('render', function() {
37523         this.view.el.addClass('x-grid-cal'); 
37524         
37525         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37526
37527     },this);
37528     
37529     if (!Roo.grid.Calendar.style) {
37530         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37531             
37532             
37533             '.x-grid-cal .x-grid-col' :  {
37534                 height: 'auto !important',
37535                 'vertical-align': 'top'
37536             },
37537             '.x-grid-cal  .fc-event-hori' : {
37538                 height: '14px'
37539             }
37540              
37541             
37542         }, Roo.id());
37543     }
37544
37545     
37546     
37547 };
37548 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37549     /**
37550      * @cfg {Store} eventStore The store that loads events.
37551      */
37552     eventStore : 25,
37553
37554      
37555     activeDate : false,
37556     startDay : 0,
37557     autoWidth : true,
37558     monitorWindowResize : false,
37559
37560     
37561     resizeColumns : function() {
37562         var col = (this.view.el.getWidth() / 7) - 3;
37563         // loop through cols, and setWidth
37564         for(var i =0 ; i < 7 ; i++){
37565             this.cm.setColumnWidth(i, col);
37566         }
37567     },
37568      setDate :function(date) {
37569         
37570         Roo.log('setDate?');
37571         
37572         this.resizeColumns();
37573         var vd = this.activeDate;
37574         this.activeDate = date;
37575 //        if(vd && this.el){
37576 //            var t = date.getTime();
37577 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37578 //                Roo.log('using add remove');
37579 //                
37580 //                this.fireEvent('monthchange', this, date);
37581 //                
37582 //                this.cells.removeClass("fc-state-highlight");
37583 //                this.cells.each(function(c){
37584 //                   if(c.dateValue == t){
37585 //                       c.addClass("fc-state-highlight");
37586 //                       setTimeout(function(){
37587 //                            try{c.dom.firstChild.focus();}catch(e){}
37588 //                       }, 50);
37589 //                       return false;
37590 //                   }
37591 //                   return true;
37592 //                });
37593 //                return;
37594 //            }
37595 //        }
37596         
37597         var days = date.getDaysInMonth();
37598         
37599         var firstOfMonth = date.getFirstDateOfMonth();
37600         var startingPos = firstOfMonth.getDay()-this.startDay;
37601         
37602         if(startingPos < this.startDay){
37603             startingPos += 7;
37604         }
37605         
37606         var pm = date.add(Date.MONTH, -1);
37607         var prevStart = pm.getDaysInMonth()-startingPos;
37608 //        
37609         
37610         
37611         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37612         
37613         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37614         //this.cells.addClassOnOver('fc-state-hover');
37615         
37616         var cells = this.cells.elements;
37617         var textEls = this.textNodes;
37618         
37619         //Roo.each(cells, function(cell){
37620         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37621         //});
37622         
37623         days += startingPos;
37624
37625         // convert everything to numbers so it's fast
37626         var day = 86400000;
37627         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37628         //Roo.log(d);
37629         //Roo.log(pm);
37630         //Roo.log(prevStart);
37631         
37632         var today = new Date().clearTime().getTime();
37633         var sel = date.clearTime().getTime();
37634         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37635         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37636         var ddMatch = this.disabledDatesRE;
37637         var ddText = this.disabledDatesText;
37638         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37639         var ddaysText = this.disabledDaysText;
37640         var format = this.format;
37641         
37642         var setCellClass = function(cal, cell){
37643             
37644             //Roo.log('set Cell Class');
37645             cell.title = "";
37646             var t = d.getTime();
37647             
37648             //Roo.log(d);
37649             
37650             
37651             cell.dateValue = t;
37652             if(t == today){
37653                 cell.className += " fc-today";
37654                 cell.className += " fc-state-highlight";
37655                 cell.title = cal.todayText;
37656             }
37657             if(t == sel){
37658                 // disable highlight in other month..
37659                 cell.className += " fc-state-highlight";
37660                 
37661             }
37662             // disabling
37663             if(t < min) {
37664                 //cell.className = " fc-state-disabled";
37665                 cell.title = cal.minText;
37666                 return;
37667             }
37668             if(t > max) {
37669                 //cell.className = " fc-state-disabled";
37670                 cell.title = cal.maxText;
37671                 return;
37672             }
37673             if(ddays){
37674                 if(ddays.indexOf(d.getDay()) != -1){
37675                     // cell.title = ddaysText;
37676                    // cell.className = " fc-state-disabled";
37677                 }
37678             }
37679             if(ddMatch && format){
37680                 var fvalue = d.dateFormat(format);
37681                 if(ddMatch.test(fvalue)){
37682                     cell.title = ddText.replace("%0", fvalue);
37683                    cell.className = " fc-state-disabled";
37684                 }
37685             }
37686             
37687             if (!cell.initialClassName) {
37688                 cell.initialClassName = cell.dom.className;
37689             }
37690             
37691             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37692         };
37693
37694         var i = 0;
37695         
37696         for(; i < startingPos; i++) {
37697             cells[i].dayName =  (++prevStart);
37698             Roo.log(textEls[i]);
37699             d.setDate(d.getDate()+1);
37700             
37701             //cells[i].className = "fc-past fc-other-month";
37702             setCellClass(this, cells[i]);
37703         }
37704         
37705         var intDay = 0;
37706         
37707         for(; i < days; i++){
37708             intDay = i - startingPos + 1;
37709             cells[i].dayName =  (intDay);
37710             d.setDate(d.getDate()+1);
37711             
37712             cells[i].className = ''; // "x-date-active";
37713             setCellClass(this, cells[i]);
37714         }
37715         var extraDays = 0;
37716         
37717         for(; i < 42; i++) {
37718             //textEls[i].innerHTML = (++extraDays);
37719             
37720             d.setDate(d.getDate()+1);
37721             cells[i].dayName = (++extraDays);
37722             cells[i].className = "fc-future fc-other-month";
37723             setCellClass(this, cells[i]);
37724         }
37725         
37726         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37727         
37728         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37729         
37730         // this will cause all the cells to mis
37731         var rows= [];
37732         var i =0;
37733         for (var r = 0;r < 6;r++) {
37734             for (var c =0;c < 7;c++) {
37735                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37736             }    
37737         }
37738         
37739         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37740         for(i=0;i<cells.length;i++) {
37741             
37742             this.cells.elements[i].dayName = cells[i].dayName ;
37743             this.cells.elements[i].className = cells[i].className;
37744             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37745             this.cells.elements[i].title = cells[i].title ;
37746             this.cells.elements[i].dateValue = cells[i].dateValue ;
37747         }
37748         
37749         
37750         
37751         
37752         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37753         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37754         
37755         ////if(totalRows != 6){
37756             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37757            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37758        // }
37759         
37760         this.fireEvent('monthchange', this, date);
37761         
37762         
37763     },
37764  /**
37765      * Returns the grid's SelectionModel.
37766      * @return {SelectionModel}
37767      */
37768     getSelectionModel : function(){
37769         if(!this.selModel){
37770             this.selModel = new Roo.grid.CellSelectionModel();
37771         }
37772         return this.selModel;
37773     },
37774
37775     load: function() {
37776         this.eventStore.load()
37777         
37778         
37779         
37780     },
37781     
37782     findCell : function(dt) {
37783         dt = dt.clearTime().getTime();
37784         var ret = false;
37785         this.cells.each(function(c){
37786             //Roo.log("check " +c.dateValue + '?=' + dt);
37787             if(c.dateValue == dt){
37788                 ret = c;
37789                 return false;
37790             }
37791             return true;
37792         });
37793         
37794         return ret;
37795     },
37796     
37797     findCells : function(rec) {
37798         var s = rec.data.start_dt.clone().clearTime().getTime();
37799        // Roo.log(s);
37800         var e= rec.data.end_dt.clone().clearTime().getTime();
37801        // Roo.log(e);
37802         var ret = [];
37803         this.cells.each(function(c){
37804              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37805             
37806             if(c.dateValue > e){
37807                 return ;
37808             }
37809             if(c.dateValue < s){
37810                 return ;
37811             }
37812             ret.push(c);
37813         });
37814         
37815         return ret;    
37816     },
37817     
37818     findBestRow: function(cells)
37819     {
37820         var ret = 0;
37821         
37822         for (var i =0 ; i < cells.length;i++) {
37823             ret  = Math.max(cells[i].rows || 0,ret);
37824         }
37825         return ret;
37826         
37827     },
37828     
37829     
37830     addItem : function(rec)
37831     {
37832         // look for vertical location slot in
37833         var cells = this.findCells(rec);
37834         
37835         rec.row = this.findBestRow(cells);
37836         
37837         // work out the location.
37838         
37839         var crow = false;
37840         var rows = [];
37841         for(var i =0; i < cells.length; i++) {
37842             if (!crow) {
37843                 crow = {
37844                     start : cells[i],
37845                     end :  cells[i]
37846                 };
37847                 continue;
37848             }
37849             if (crow.start.getY() == cells[i].getY()) {
37850                 // on same row.
37851                 crow.end = cells[i];
37852                 continue;
37853             }
37854             // different row.
37855             rows.push(crow);
37856             crow = {
37857                 start: cells[i],
37858                 end : cells[i]
37859             };
37860             
37861         }
37862         
37863         rows.push(crow);
37864         rec.els = [];
37865         rec.rows = rows;
37866         rec.cells = cells;
37867         for (var i = 0; i < cells.length;i++) {
37868             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37869             
37870         }
37871         
37872         
37873     },
37874     
37875     clearEvents: function() {
37876         
37877         if (!this.eventStore.getCount()) {
37878             return;
37879         }
37880         // reset number of rows in cells.
37881         Roo.each(this.cells.elements, function(c){
37882             c.rows = 0;
37883         });
37884         
37885         this.eventStore.each(function(e) {
37886             this.clearEvent(e);
37887         },this);
37888         
37889     },
37890     
37891     clearEvent : function(ev)
37892     {
37893         if (ev.els) {
37894             Roo.each(ev.els, function(el) {
37895                 el.un('mouseenter' ,this.onEventEnter, this);
37896                 el.un('mouseleave' ,this.onEventLeave, this);
37897                 el.remove();
37898             },this);
37899             ev.els = [];
37900         }
37901     },
37902     
37903     
37904     renderEvent : function(ev,ctr) {
37905         if (!ctr) {
37906              ctr = this.view.el.select('.fc-event-container',true).first();
37907         }
37908         
37909          
37910         this.clearEvent(ev);
37911             //code
37912        
37913         
37914         
37915         ev.els = [];
37916         var cells = ev.cells;
37917         var rows = ev.rows;
37918         this.fireEvent('eventrender', this, ev);
37919         
37920         for(var i =0; i < rows.length; i++) {
37921             
37922             cls = '';
37923             if (i == 0) {
37924                 cls += ' fc-event-start';
37925             }
37926             if ((i+1) == rows.length) {
37927                 cls += ' fc-event-end';
37928             }
37929             
37930             //Roo.log(ev.data);
37931             // how many rows should it span..
37932             var cg = this.eventTmpl.append(ctr,Roo.apply({
37933                 fccls : cls
37934                 
37935             }, ev.data) , true);
37936             
37937             
37938             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37939             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37940             cg.on('click', this.onEventClick, this, ev);
37941             
37942             ev.els.push(cg);
37943             
37944             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37945             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37946             //Roo.log(cg);
37947              
37948             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37949             cg.setWidth(ebox.right - sbox.x -2);
37950         }
37951     },
37952     
37953     renderEvents: function()
37954     {   
37955         // first make sure there is enough space..
37956         
37957         if (!this.eventTmpl) {
37958             this.eventTmpl = new Roo.Template(
37959                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37960                     '<div class="fc-event-inner">' +
37961                         '<span class="fc-event-time">{time}</span>' +
37962                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37963                     '</div>' +
37964                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37965                 '</div>'
37966             );
37967                 
37968         }
37969                
37970         
37971         
37972         this.cells.each(function(c) {
37973             //Roo.log(c.select('.fc-day-content div',true).first());
37974             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37975         });
37976         
37977         var ctr = this.view.el.select('.fc-event-container',true).first();
37978         
37979         var cls;
37980         this.eventStore.each(function(ev){
37981             
37982             this.renderEvent(ev);
37983              
37984              
37985         }, this);
37986         this.view.layout();
37987         
37988     },
37989     
37990     onEventEnter: function (e, el,event,d) {
37991         this.fireEvent('evententer', this, el, event);
37992     },
37993     
37994     onEventLeave: function (e, el,event,d) {
37995         this.fireEvent('eventleave', this, el, event);
37996     },
37997     
37998     onEventClick: function (e, el,event,d) {
37999         this.fireEvent('eventclick', this, el, event);
38000     },
38001     
38002     onMonthChange: function () {
38003         this.store.load();
38004     },
38005     
38006     onLoad: function () {
38007         
38008         //Roo.log('calendar onload');
38009 //         
38010         if(this.eventStore.getCount() > 0){
38011             
38012            
38013             
38014             this.eventStore.each(function(d){
38015                 
38016                 
38017                 // FIXME..
38018                 var add =   d.data;
38019                 if (typeof(add.end_dt) == 'undefined')  {
38020                     Roo.log("Missing End time in calendar data: ");
38021                     Roo.log(d);
38022                     return;
38023                 }
38024                 if (typeof(add.start_dt) == 'undefined')  {
38025                     Roo.log("Missing Start time in calendar data: ");
38026                     Roo.log(d);
38027                     return;
38028                 }
38029                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38030                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38031                 add.id = add.id || d.id;
38032                 add.title = add.title || '??';
38033                 
38034                 this.addItem(d);
38035                 
38036              
38037             },this);
38038         }
38039         
38040         this.renderEvents();
38041     }
38042     
38043
38044 });
38045 /*
38046  grid : {
38047                 xtype: 'Grid',
38048                 xns: Roo.grid,
38049                 listeners : {
38050                     render : function ()
38051                     {
38052                         _this.grid = this;
38053                         
38054                         if (!this.view.el.hasClass('course-timesheet')) {
38055                             this.view.el.addClass('course-timesheet');
38056                         }
38057                         if (this.tsStyle) {
38058                             this.ds.load({});
38059                             return; 
38060                         }
38061                         Roo.log('width');
38062                         Roo.log(_this.grid.view.el.getWidth());
38063                         
38064                         
38065                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38066                             '.course-timesheet .x-grid-row' : {
38067                                 height: '80px'
38068                             },
38069                             '.x-grid-row td' : {
38070                                 'vertical-align' : 0
38071                             },
38072                             '.course-edit-link' : {
38073                                 'color' : 'blue',
38074                                 'text-overflow' : 'ellipsis',
38075                                 'overflow' : 'hidden',
38076                                 'white-space' : 'nowrap',
38077                                 'cursor' : 'pointer'
38078                             },
38079                             '.sub-link' : {
38080                                 'color' : 'green'
38081                             },
38082                             '.de-act-sup-link' : {
38083                                 'color' : 'purple',
38084                                 'text-decoration' : 'line-through'
38085                             },
38086                             '.de-act-link' : {
38087                                 'color' : 'red',
38088                                 'text-decoration' : 'line-through'
38089                             },
38090                             '.course-timesheet .course-highlight' : {
38091                                 'border-top-style': 'dashed !important',
38092                                 'border-bottom-bottom': 'dashed !important'
38093                             },
38094                             '.course-timesheet .course-item' : {
38095                                 'font-family'   : 'tahoma, arial, helvetica',
38096                                 'font-size'     : '11px',
38097                                 'overflow'      : 'hidden',
38098                                 'padding-left'  : '10px',
38099                                 'padding-right' : '10px',
38100                                 'padding-top' : '10px' 
38101                             }
38102                             
38103                         }, Roo.id());
38104                                 this.ds.load({});
38105                     }
38106                 },
38107                 autoWidth : true,
38108                 monitorWindowResize : false,
38109                 cellrenderer : function(v,x,r)
38110                 {
38111                     return v;
38112                 },
38113                 sm : {
38114                     xtype: 'CellSelectionModel',
38115                     xns: Roo.grid
38116                 },
38117                 dataSource : {
38118                     xtype: 'Store',
38119                     xns: Roo.data,
38120                     listeners : {
38121                         beforeload : function (_self, options)
38122                         {
38123                             options.params = options.params || {};
38124                             options.params._month = _this.monthField.getValue();
38125                             options.params.limit = 9999;
38126                             options.params['sort'] = 'when_dt';    
38127                             options.params['dir'] = 'ASC';    
38128                             this.proxy.loadResponse = this.loadResponse;
38129                             Roo.log("load?");
38130                             //this.addColumns();
38131                         },
38132                         load : function (_self, records, options)
38133                         {
38134                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38135                                 // if you click on the translation.. you can edit it...
38136                                 var el = Roo.get(this);
38137                                 var id = el.dom.getAttribute('data-id');
38138                                 var d = el.dom.getAttribute('data-date');
38139                                 var t = el.dom.getAttribute('data-time');
38140                                 //var id = this.child('span').dom.textContent;
38141                                 
38142                                 //Roo.log(this);
38143                                 Pman.Dialog.CourseCalendar.show({
38144                                     id : id,
38145                                     when_d : d,
38146                                     when_t : t,
38147                                     productitem_active : id ? 1 : 0
38148                                 }, function() {
38149                                     _this.grid.ds.load({});
38150                                 });
38151                            
38152                            });
38153                            
38154                            _this.panel.fireEvent('resize', [ '', '' ]);
38155                         }
38156                     },
38157                     loadResponse : function(o, success, response){
38158                             // this is overridden on before load..
38159                             
38160                             Roo.log("our code?");       
38161                             //Roo.log(success);
38162                             //Roo.log(response)
38163                             delete this.activeRequest;
38164                             if(!success){
38165                                 this.fireEvent("loadexception", this, o, response);
38166                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38167                                 return;
38168                             }
38169                             var result;
38170                             try {
38171                                 result = o.reader.read(response);
38172                             }catch(e){
38173                                 Roo.log("load exception?");
38174                                 this.fireEvent("loadexception", this, o, response, e);
38175                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38176                                 return;
38177                             }
38178                             Roo.log("ready...");        
38179                             // loop through result.records;
38180                             // and set this.tdate[date] = [] << array of records..
38181                             _this.tdata  = {};
38182                             Roo.each(result.records, function(r){
38183                                 //Roo.log(r.data);
38184                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38185                                     _this.tdata[r.data.when_dt.format('j')] = [];
38186                                 }
38187                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38188                             });
38189                             
38190                             //Roo.log(_this.tdata);
38191                             
38192                             result.records = [];
38193                             result.totalRecords = 6;
38194                     
38195                             // let's generate some duumy records for the rows.
38196                             //var st = _this.dateField.getValue();
38197                             
38198                             // work out monday..
38199                             //st = st.add(Date.DAY, -1 * st.format('w'));
38200                             
38201                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38202                             
38203                             var firstOfMonth = date.getFirstDayOfMonth();
38204                             var days = date.getDaysInMonth();
38205                             var d = 1;
38206                             var firstAdded = false;
38207                             for (var i = 0; i < result.totalRecords ; i++) {
38208                                 //var d= st.add(Date.DAY, i);
38209                                 var row = {};
38210                                 var added = 0;
38211                                 for(var w = 0 ; w < 7 ; w++){
38212                                     if(!firstAdded && firstOfMonth != w){
38213                                         continue;
38214                                     }
38215                                     if(d > days){
38216                                         continue;
38217                                     }
38218                                     firstAdded = true;
38219                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38220                                     row['weekday'+w] = String.format(
38221                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38222                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38223                                                     d,
38224                                                     date.format('Y-m-')+dd
38225                                                 );
38226                                     added++;
38227                                     if(typeof(_this.tdata[d]) != 'undefined'){
38228                                         Roo.each(_this.tdata[d], function(r){
38229                                             var is_sub = '';
38230                                             var deactive = '';
38231                                             var id = r.id;
38232                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38233                                             if(r.parent_id*1>0){
38234                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38235                                                 id = r.parent_id;
38236                                             }
38237                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38238                                                 deactive = 'de-act-link';
38239                                             }
38240                                             
38241                                             row['weekday'+w] += String.format(
38242                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38243                                                     id, //0
38244                                                     r.product_id_name, //1
38245                                                     r.when_dt.format('h:ia'), //2
38246                                                     is_sub, //3
38247                                                     deactive, //4
38248                                                     desc // 5
38249                                             );
38250                                         });
38251                                     }
38252                                     d++;
38253                                 }
38254                                 
38255                                 // only do this if something added..
38256                                 if(added > 0){ 
38257                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38258                                 }
38259                                 
38260                                 
38261                                 // push it twice. (second one with an hour..
38262                                 
38263                             }
38264                             //Roo.log(result);
38265                             this.fireEvent("load", this, o, o.request.arg);
38266                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38267                         },
38268                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38269                     proxy : {
38270                         xtype: 'HttpProxy',
38271                         xns: Roo.data,
38272                         method : 'GET',
38273                         url : baseURL + '/Roo/Shop_course.php'
38274                     },
38275                     reader : {
38276                         xtype: 'JsonReader',
38277                         xns: Roo.data,
38278                         id : 'id',
38279                         fields : [
38280                             {
38281                                 'name': 'id',
38282                                 'type': 'int'
38283                             },
38284                             {
38285                                 'name': 'when_dt',
38286                                 'type': 'string'
38287                             },
38288                             {
38289                                 'name': 'end_dt',
38290                                 'type': 'string'
38291                             },
38292                             {
38293                                 'name': 'parent_id',
38294                                 'type': 'int'
38295                             },
38296                             {
38297                                 'name': 'product_id',
38298                                 'type': 'int'
38299                             },
38300                             {
38301                                 'name': 'productitem_id',
38302                                 'type': 'int'
38303                             },
38304                             {
38305                                 'name': 'guid',
38306                                 'type': 'int'
38307                             }
38308                         ]
38309                     }
38310                 },
38311                 toolbar : {
38312                     xtype: 'Toolbar',
38313                     xns: Roo,
38314                     items : [
38315                         {
38316                             xtype: 'Button',
38317                             xns: Roo.Toolbar,
38318                             listeners : {
38319                                 click : function (_self, e)
38320                                 {
38321                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38322                                     sd.setMonth(sd.getMonth()-1);
38323                                     _this.monthField.setValue(sd.format('Y-m-d'));
38324                                     _this.grid.ds.load({});
38325                                 }
38326                             },
38327                             text : "Back"
38328                         },
38329                         {
38330                             xtype: 'Separator',
38331                             xns: Roo.Toolbar
38332                         },
38333                         {
38334                             xtype: 'MonthField',
38335                             xns: Roo.form,
38336                             listeners : {
38337                                 render : function (_self)
38338                                 {
38339                                     _this.monthField = _self;
38340                                    // _this.monthField.set  today
38341                                 },
38342                                 select : function (combo, date)
38343                                 {
38344                                     _this.grid.ds.load({});
38345                                 }
38346                             },
38347                             value : (function() { return new Date(); })()
38348                         },
38349                         {
38350                             xtype: 'Separator',
38351                             xns: Roo.Toolbar
38352                         },
38353                         {
38354                             xtype: 'TextItem',
38355                             xns: Roo.Toolbar,
38356                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38357                         },
38358                         {
38359                             xtype: 'Fill',
38360                             xns: Roo.Toolbar
38361                         },
38362                         {
38363                             xtype: 'Button',
38364                             xns: Roo.Toolbar,
38365                             listeners : {
38366                                 click : function (_self, e)
38367                                 {
38368                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38369                                     sd.setMonth(sd.getMonth()+1);
38370                                     _this.monthField.setValue(sd.format('Y-m-d'));
38371                                     _this.grid.ds.load({});
38372                                 }
38373                             },
38374                             text : "Next"
38375                         }
38376                     ]
38377                 },
38378                  
38379             }
38380         };
38381         
38382         *//*
38383  * Based on:
38384  * Ext JS Library 1.1.1
38385  * Copyright(c) 2006-2007, Ext JS, LLC.
38386  *
38387  * Originally Released Under LGPL - original licence link has changed is not relivant.
38388  *
38389  * Fork - LGPL
38390  * <script type="text/javascript">
38391  */
38392  
38393 /**
38394  * @class Roo.LoadMask
38395  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38396  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38397  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38398  * element's UpdateManager load indicator and will be destroyed after the initial load.
38399  * @constructor
38400  * Create a new LoadMask
38401  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38402  * @param {Object} config The config object
38403  */
38404 Roo.LoadMask = function(el, config){
38405     this.el = Roo.get(el);
38406     Roo.apply(this, config);
38407     if(this.store){
38408         this.store.on('beforeload', this.onBeforeLoad, this);
38409         this.store.on('load', this.onLoad, this);
38410         this.store.on('loadexception', this.onLoadException, this);
38411         this.removeMask = false;
38412     }else{
38413         var um = this.el.getUpdateManager();
38414         um.showLoadIndicator = false; // disable the default indicator
38415         um.on('beforeupdate', this.onBeforeLoad, this);
38416         um.on('update', this.onLoad, this);
38417         um.on('failure', this.onLoad, this);
38418         this.removeMask = true;
38419     }
38420 };
38421
38422 Roo.LoadMask.prototype = {
38423     /**
38424      * @cfg {Boolean} removeMask
38425      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38426      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38427      */
38428     removeMask : false,
38429     /**
38430      * @cfg {String} msg
38431      * The text to display in a centered loading message box (defaults to 'Loading...')
38432      */
38433     msg : 'Loading...',
38434     /**
38435      * @cfg {String} msgCls
38436      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38437      */
38438     msgCls : 'x-mask-loading',
38439
38440     /**
38441      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38442      * @type Boolean
38443      */
38444     disabled: false,
38445
38446     /**
38447      * Disables the mask to prevent it from being displayed
38448      */
38449     disable : function(){
38450        this.disabled = true;
38451     },
38452
38453     /**
38454      * Enables the mask so that it can be displayed
38455      */
38456     enable : function(){
38457         this.disabled = false;
38458     },
38459     
38460     onLoadException : function()
38461     {
38462         Roo.log(arguments);
38463         
38464         if (typeof(arguments[3]) != 'undefined') {
38465             Roo.MessageBox.alert("Error loading",arguments[3]);
38466         } 
38467         /*
38468         try {
38469             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38470                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38471             }   
38472         } catch(e) {
38473             
38474         }
38475         */
38476     
38477         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38478     },
38479     // private
38480     onLoad : function()
38481     {
38482         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38483     },
38484
38485     // private
38486     onBeforeLoad : function(){
38487         if(!this.disabled){
38488             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38489         }
38490     },
38491
38492     // private
38493     destroy : function(){
38494         if(this.store){
38495             this.store.un('beforeload', this.onBeforeLoad, this);
38496             this.store.un('load', this.onLoad, this);
38497             this.store.un('loadexception', this.onLoadException, this);
38498         }else{
38499             var um = this.el.getUpdateManager();
38500             um.un('beforeupdate', this.onBeforeLoad, this);
38501             um.un('update', this.onLoad, this);
38502             um.un('failure', this.onLoad, this);
38503         }
38504     }
38505 };/*
38506  * Based on:
38507  * Ext JS Library 1.1.1
38508  * Copyright(c) 2006-2007, Ext JS, LLC.
38509  *
38510  * Originally Released Under LGPL - original licence link has changed is not relivant.
38511  *
38512  * Fork - LGPL
38513  * <script type="text/javascript">
38514  */
38515
38516
38517 /**
38518  * @class Roo.XTemplate
38519  * @extends Roo.Template
38520  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38521 <pre><code>
38522 var t = new Roo.XTemplate(
38523         '&lt;select name="{name}"&gt;',
38524                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38525         '&lt;/select&gt;'
38526 );
38527  
38528 // then append, applying the master template values
38529  </code></pre>
38530  *
38531  * Supported features:
38532  *
38533  *  Tags:
38534
38535 <pre><code>
38536       {a_variable} - output encoded.
38537       {a_variable.format:("Y-m-d")} - call a method on the variable
38538       {a_variable:raw} - unencoded output
38539       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38540       {a_variable:this.method_on_template(...)} - call a method on the template object.
38541  
38542 </code></pre>
38543  *  The tpl tag:
38544 <pre><code>
38545         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38546         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38547         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38548         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38549   
38550         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38551         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38552 </code></pre>
38553  *      
38554  */
38555 Roo.XTemplate = function()
38556 {
38557     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38558     if (this.html) {
38559         this.compile();
38560     }
38561 };
38562
38563
38564 Roo.extend(Roo.XTemplate, Roo.Template, {
38565
38566     /**
38567      * The various sub templates
38568      */
38569     tpls : false,
38570     /**
38571      *
38572      * basic tag replacing syntax
38573      * WORD:WORD()
38574      *
38575      * // you can fake an object call by doing this
38576      *  x.t:(test,tesT) 
38577      * 
38578      */
38579     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38580
38581     /**
38582      * compile the template
38583      *
38584      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38585      *
38586      */
38587     compile: function()
38588     {
38589         var s = this.html;
38590      
38591         s = ['<tpl>', s, '</tpl>'].join('');
38592     
38593         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38594             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38595             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38596             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38597             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38598             m,
38599             id     = 0,
38600             tpls   = [];
38601     
38602         while(true == !!(m = s.match(re))){
38603             var forMatch   = m[0].match(nameRe),
38604                 ifMatch   = m[0].match(ifRe),
38605                 execMatch   = m[0].match(execRe),
38606                 namedMatch   = m[0].match(namedRe),
38607                 
38608                 exp  = null, 
38609                 fn   = null,
38610                 exec = null,
38611                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38612                 
38613             if (ifMatch) {
38614                 // if - puts fn into test..
38615                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38616                 if(exp){
38617                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38618                 }
38619             }
38620             
38621             if (execMatch) {
38622                 // exec - calls a function... returns empty if true is  returned.
38623                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38624                 if(exp){
38625                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38626                 }
38627             }
38628             
38629             
38630             if (name) {
38631                 // for = 
38632                 switch(name){
38633                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38634                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38635                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38636                 }
38637             }
38638             var uid = namedMatch ? namedMatch[1] : id;
38639             
38640             
38641             tpls.push({
38642                 id:     namedMatch ? namedMatch[1] : id,
38643                 target: name,
38644                 exec:   exec,
38645                 test:   fn,
38646                 body:   m[1] || ''
38647             });
38648             if (namedMatch) {
38649                 s = s.replace(m[0], '');
38650             } else { 
38651                 s = s.replace(m[0], '{xtpl'+ id + '}');
38652             }
38653             ++id;
38654         }
38655         this.tpls = [];
38656         for(var i = tpls.length-1; i >= 0; --i){
38657             this.compileTpl(tpls[i]);
38658             this.tpls[tpls[i].id] = tpls[i];
38659         }
38660         this.master = tpls[tpls.length-1];
38661         return this;
38662     },
38663     /**
38664      * same as applyTemplate, except it's done to one of the subTemplates
38665      * when using named templates, you can do:
38666      *
38667      * var str = pl.applySubTemplate('your-name', values);
38668      *
38669      * 
38670      * @param {Number} id of the template
38671      * @param {Object} values to apply to template
38672      * @param {Object} parent (normaly the instance of this object)
38673      */
38674     applySubTemplate : function(id, values, parent)
38675     {
38676         
38677         
38678         var t = this.tpls[id];
38679         
38680         
38681         try { 
38682             if(t.test && !t.test.call(this, values, parent)){
38683                 return '';
38684             }
38685         } catch(e) {
38686             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38687             Roo.log(e.toString());
38688             Roo.log(t.test);
38689             return ''
38690         }
38691         try { 
38692             
38693             if(t.exec && t.exec.call(this, values, parent)){
38694                 return '';
38695             }
38696         } catch(e) {
38697             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38698             Roo.log(e.toString());
38699             Roo.log(t.exec);
38700             return ''
38701         }
38702         try {
38703             var vs = t.target ? t.target.call(this, values, parent) : values;
38704             parent = t.target ? values : parent;
38705             if(t.target && vs instanceof Array){
38706                 var buf = [];
38707                 for(var i = 0, len = vs.length; i < len; i++){
38708                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38709                 }
38710                 return buf.join('');
38711             }
38712             return t.compiled.call(this, vs, parent);
38713         } catch (e) {
38714             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38715             Roo.log(e.toString());
38716             Roo.log(t.compiled);
38717             return '';
38718         }
38719     },
38720
38721     compileTpl : function(tpl)
38722     {
38723         var fm = Roo.util.Format;
38724         var useF = this.disableFormats !== true;
38725         var sep = Roo.isGecko ? "+" : ",";
38726         var undef = function(str) {
38727             Roo.log("Property not found :"  + str);
38728             return '';
38729         };
38730         
38731         var fn = function(m, name, format, args)
38732         {
38733             //Roo.log(arguments);
38734             args = args ? args.replace(/\\'/g,"'") : args;
38735             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38736             if (typeof(format) == 'undefined') {
38737                 format= 'htmlEncode';
38738             }
38739             if (format == 'raw' ) {
38740                 format = false;
38741             }
38742             
38743             if(name.substr(0, 4) == 'xtpl'){
38744                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38745             }
38746             
38747             // build an array of options to determine if value is undefined..
38748             
38749             // basically get 'xxxx.yyyy' then do
38750             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38751             //    (function () { Roo.log("Property not found"); return ''; })() :
38752             //    ......
38753             
38754             var udef_ar = [];
38755             var lookfor = '';
38756             Roo.each(name.split('.'), function(st) {
38757                 lookfor += (lookfor.length ? '.': '') + st;
38758                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38759             });
38760             
38761             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38762             
38763             
38764             if(format && useF){
38765                 
38766                 args = args ? ',' + args : "";
38767                  
38768                 if(format.substr(0, 5) != "this."){
38769                     format = "fm." + format + '(';
38770                 }else{
38771                     format = 'this.call("'+ format.substr(5) + '", ';
38772                     args = ", values";
38773                 }
38774                 
38775                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38776             }
38777              
38778             if (args.length) {
38779                 // called with xxyx.yuu:(test,test)
38780                 // change to ()
38781                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38782             }
38783             // raw.. - :raw modifier..
38784             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38785             
38786         };
38787         var body;
38788         // branched to use + in gecko and [].join() in others
38789         if(Roo.isGecko){
38790             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38791                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38792                     "';};};";
38793         }else{
38794             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38795             body.push(tpl.body.replace(/(\r\n|\n)/g,
38796                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38797             body.push("'].join('');};};");
38798             body = body.join('');
38799         }
38800         
38801         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38802        
38803         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38804         eval(body);
38805         
38806         return this;
38807     },
38808
38809     applyTemplate : function(values){
38810         return this.master.compiled.call(this, values, {});
38811         //var s = this.subs;
38812     },
38813
38814     apply : function(){
38815         return this.applyTemplate.apply(this, arguments);
38816     }
38817
38818  });
38819
38820 Roo.XTemplate.from = function(el){
38821     el = Roo.getDom(el);
38822     return new Roo.XTemplate(el.value || el.innerHTML);
38823 };