Fix #6901 - CRM - allow comments in HTML for IE
[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     
20615     allowComments: false,
20616     // id of frame..
20617     frameId: false,
20618     
20619     // private properties
20620     validationEvent : false,
20621     deferHeight: true,
20622     initialized : false,
20623     activated : false,
20624     sourceEditMode : false,
20625     onFocus : Roo.emptyFn,
20626     iframePad:3,
20627     hideMode:'offsets',
20628     
20629     clearUp: true,
20630     
20631     // blacklist + whitelisted elements..
20632     black: false,
20633     white: false,
20634      
20635     bodyCls : '',
20636
20637     /**
20638      * Protected method that will not generally be called directly. It
20639      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20640      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20641      */
20642     getDocMarkup : function(){
20643         // body styles..
20644         var st = '';
20645         
20646         // inherit styels from page...?? 
20647         if (this.stylesheets === false) {
20648             
20649             Roo.get(document.head).select('style').each(function(node) {
20650                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20651             });
20652             
20653             Roo.get(document.head).select('link').each(function(node) { 
20654                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20655             });
20656             
20657         } else if (!this.stylesheets.length) {
20658                 // simple..
20659                 st = '<style type="text/css">' +
20660                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20661                    '</style>';
20662         } else {
20663             for (var i in this.stylesheets) { 
20664                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20665             }
20666             
20667         }
20668         
20669         st +=  '<style type="text/css">' +
20670             'IMG { cursor: pointer } ' +
20671         '</style>';
20672
20673         var cls = 'roo-htmleditor-body';
20674         
20675         if(this.bodyCls.length){
20676             cls += ' ' + this.bodyCls;
20677         }
20678         
20679         return '<html><head>' + st  +
20680             //<style type="text/css">' +
20681             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20682             //'</style>' +
20683             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20684     },
20685
20686     // private
20687     onRender : function(ct, position)
20688     {
20689         var _t = this;
20690         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20691         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20692         
20693         
20694         this.el.dom.style.border = '0 none';
20695         this.el.dom.setAttribute('tabIndex', -1);
20696         this.el.addClass('x-hidden hide');
20697         
20698         
20699         
20700         if(Roo.isIE){ // fix IE 1px bogus margin
20701             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20702         }
20703        
20704         
20705         this.frameId = Roo.id();
20706         
20707          
20708         
20709         var iframe = this.owner.wrap.createChild({
20710             tag: 'iframe',
20711             cls: 'form-control', // bootstrap..
20712             id: this.frameId,
20713             name: this.frameId,
20714             frameBorder : 'no',
20715             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20716         }, this.el
20717         );
20718         
20719         
20720         this.iframe = iframe.dom;
20721
20722          this.assignDocWin();
20723         
20724         this.doc.designMode = 'on';
20725        
20726         this.doc.open();
20727         this.doc.write(this.getDocMarkup());
20728         this.doc.close();
20729
20730         
20731         var task = { // must defer to wait for browser to be ready
20732             run : function(){
20733                 //console.log("run task?" + this.doc.readyState);
20734                 this.assignDocWin();
20735                 if(this.doc.body || this.doc.readyState == 'complete'){
20736                     try {
20737                         this.doc.designMode="on";
20738                     } catch (e) {
20739                         return;
20740                     }
20741                     Roo.TaskMgr.stop(task);
20742                     this.initEditor.defer(10, this);
20743                 }
20744             },
20745             interval : 10,
20746             duration: 10000,
20747             scope: this
20748         };
20749         Roo.TaskMgr.start(task);
20750
20751     },
20752
20753     // private
20754     onResize : function(w, h)
20755     {
20756          Roo.log('resize: ' +w + ',' + h );
20757         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20758         if(!this.iframe){
20759             return;
20760         }
20761         if(typeof w == 'number'){
20762             
20763             this.iframe.style.width = w + 'px';
20764         }
20765         if(typeof h == 'number'){
20766             
20767             this.iframe.style.height = h + 'px';
20768             if(this.doc){
20769                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20770             }
20771         }
20772         
20773     },
20774
20775     /**
20776      * Toggles the editor between standard and source edit mode.
20777      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20778      */
20779     toggleSourceEdit : function(sourceEditMode){
20780         
20781         this.sourceEditMode = sourceEditMode === true;
20782         
20783         if(this.sourceEditMode){
20784  
20785             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20786             
20787         }else{
20788             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20789             //this.iframe.className = '';
20790             this.deferFocus();
20791         }
20792         //this.setSize(this.owner.wrap.getSize());
20793         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20794     },
20795
20796     
20797   
20798
20799     /**
20800      * Protected method that will not generally be called directly. If you need/want
20801      * custom HTML cleanup, this is the method you should override.
20802      * @param {String} html The HTML to be cleaned
20803      * return {String} The cleaned HTML
20804      */
20805     cleanHtml : function(html){
20806         html = String(html);
20807         if(html.length > 5){
20808             if(Roo.isSafari){ // strip safari nonsense
20809                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20810             }
20811         }
20812         if(html == '&nbsp;'){
20813             html = '';
20814         }
20815         return html;
20816     },
20817
20818     /**
20819      * HTML Editor -> Textarea
20820      * Protected method that will not generally be called directly. Syncs the contents
20821      * of the editor iframe with the textarea.
20822      */
20823     syncValue : function(){
20824         if(this.initialized){
20825             var bd = (this.doc.body || this.doc.documentElement);
20826             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20827             var html = bd.innerHTML;
20828             if(Roo.isSafari){
20829                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20830                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20831                 if(m && m[1]){
20832                     html = '<div style="'+m[0]+'">' + html + '</div>';
20833                 }
20834             }
20835             html = this.cleanHtml(html);
20836             // fix up the special chars.. normaly like back quotes in word...
20837             // however we do not want to do this with chinese..
20838             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20839                 
20840                 var cc = match.charCodeAt();
20841
20842                 // Get the character value, handling surrogate pairs
20843                 if (match.length == 2) {
20844                     // It's a surrogate pair, calculate the Unicode code point
20845                     var high = match.charCodeAt(0) - 0xD800;
20846                     var low  = match.charCodeAt(1) - 0xDC00;
20847                     cc = (high * 0x400) + low + 0x10000;
20848                 }  else if (
20849                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20850                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20851                     (cc >= 0xf900 && cc < 0xfb00 )
20852                 ) {
20853                         return match;
20854                 }  
20855          
20856                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20857                 return "&#" + cc + ";";
20858                 
20859                 
20860             });
20861             
20862             
20863              
20864             if(this.owner.fireEvent('beforesync', this, html) !== false){
20865                 this.el.dom.value = html;
20866                 this.owner.fireEvent('sync', this, html);
20867             }
20868         }
20869     },
20870
20871     /**
20872      * Protected method that will not generally be called directly. Pushes the value of the textarea
20873      * into the iframe editor.
20874      */
20875     pushValue : function(){
20876         if(this.initialized){
20877             var v = this.el.dom.value.trim();
20878             
20879 //            if(v.length < 1){
20880 //                v = '&#160;';
20881 //            }
20882             
20883             if(this.owner.fireEvent('beforepush', this, v) !== false){
20884                 var d = (this.doc.body || this.doc.documentElement);
20885                 d.innerHTML = v;
20886                 this.cleanUpPaste();
20887                 this.el.dom.value = d.innerHTML;
20888                 this.owner.fireEvent('push', this, v);
20889             }
20890         }
20891     },
20892
20893     // private
20894     deferFocus : function(){
20895         this.focus.defer(10, this);
20896     },
20897
20898     // doc'ed in Field
20899     focus : function(){
20900         if(this.win && !this.sourceEditMode){
20901             this.win.focus();
20902         }else{
20903             this.el.focus();
20904         }
20905     },
20906     
20907     assignDocWin: function()
20908     {
20909         var iframe = this.iframe;
20910         
20911          if(Roo.isIE){
20912             this.doc = iframe.contentWindow.document;
20913             this.win = iframe.contentWindow;
20914         } else {
20915 //            if (!Roo.get(this.frameId)) {
20916 //                return;
20917 //            }
20918 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20919 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20920             
20921             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20922                 return;
20923             }
20924             
20925             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20926             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20927         }
20928     },
20929     
20930     // private
20931     initEditor : function(){
20932         //console.log("INIT EDITOR");
20933         this.assignDocWin();
20934         
20935         
20936         
20937         this.doc.designMode="on";
20938         this.doc.open();
20939         this.doc.write(this.getDocMarkup());
20940         this.doc.close();
20941         
20942         var dbody = (this.doc.body || this.doc.documentElement);
20943         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20944         // this copies styles from the containing element into thsi one..
20945         // not sure why we need all of this..
20946         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20947         
20948         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20949         //ss['background-attachment'] = 'fixed'; // w3c
20950         dbody.bgProperties = 'fixed'; // ie
20951         //Roo.DomHelper.applyStyles(dbody, ss);
20952         Roo.EventManager.on(this.doc, {
20953             //'mousedown': this.onEditorEvent,
20954             'mouseup': this.onEditorEvent,
20955             'dblclick': this.onEditorEvent,
20956             'click': this.onEditorEvent,
20957             'keyup': this.onEditorEvent,
20958             buffer:100,
20959             scope: this
20960         });
20961         if(Roo.isGecko){
20962             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20963         }
20964         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20965             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20966         }
20967         this.initialized = true;
20968
20969         this.owner.fireEvent('initialize', this);
20970         this.pushValue();
20971     },
20972
20973     // private
20974     onDestroy : function(){
20975         
20976         
20977         
20978         if(this.rendered){
20979             
20980             //for (var i =0; i < this.toolbars.length;i++) {
20981             //    // fixme - ask toolbars for heights?
20982             //    this.toolbars[i].onDestroy();
20983            // }
20984             
20985             //this.wrap.dom.innerHTML = '';
20986             //this.wrap.remove();
20987         }
20988     },
20989
20990     // private
20991     onFirstFocus : function(){
20992         
20993         this.assignDocWin();
20994         
20995         
20996         this.activated = true;
20997          
20998     
20999         if(Roo.isGecko){ // prevent silly gecko errors
21000             this.win.focus();
21001             var s = this.win.getSelection();
21002             if(!s.focusNode || s.focusNode.nodeType != 3){
21003                 var r = s.getRangeAt(0);
21004                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21005                 r.collapse(true);
21006                 this.deferFocus();
21007             }
21008             try{
21009                 this.execCmd('useCSS', true);
21010                 this.execCmd('styleWithCSS', false);
21011             }catch(e){}
21012         }
21013         this.owner.fireEvent('activate', this);
21014     },
21015
21016     // private
21017     adjustFont: function(btn){
21018         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21019         //if(Roo.isSafari){ // safari
21020         //    adjust *= 2;
21021        // }
21022         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21023         if(Roo.isSafari){ // safari
21024             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21025             v =  (v < 10) ? 10 : v;
21026             v =  (v > 48) ? 48 : v;
21027             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21028             
21029         }
21030         
21031         
21032         v = Math.max(1, v+adjust);
21033         
21034         this.execCmd('FontSize', v  );
21035     },
21036
21037     onEditorEvent : function(e)
21038     {
21039         this.owner.fireEvent('editorevent', this, e);
21040       //  this.updateToolbar();
21041         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21042     },
21043
21044     insertTag : function(tg)
21045     {
21046         // could be a bit smarter... -> wrap the current selected tRoo..
21047         if (tg.toLowerCase() == 'span' ||
21048             tg.toLowerCase() == 'code' ||
21049             tg.toLowerCase() == 'sup' ||
21050             tg.toLowerCase() == 'sub' 
21051             ) {
21052             
21053             range = this.createRange(this.getSelection());
21054             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21055             wrappingNode.appendChild(range.extractContents());
21056             range.insertNode(wrappingNode);
21057
21058             return;
21059             
21060             
21061             
21062         }
21063         this.execCmd("formatblock",   tg);
21064         
21065     },
21066     
21067     insertText : function(txt)
21068     {
21069         
21070         
21071         var range = this.createRange();
21072         range.deleteContents();
21073                //alert(Sender.getAttribute('label'));
21074                
21075         range.insertNode(this.doc.createTextNode(txt));
21076     } ,
21077     
21078      
21079
21080     /**
21081      * Executes a Midas editor command on the editor document and performs necessary focus and
21082      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21083      * @param {String} cmd The Midas command
21084      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21085      */
21086     relayCmd : function(cmd, value){
21087         this.win.focus();
21088         this.execCmd(cmd, value);
21089         this.owner.fireEvent('editorevent', this);
21090         //this.updateToolbar();
21091         this.owner.deferFocus();
21092     },
21093
21094     /**
21095      * Executes a Midas editor command directly on the editor document.
21096      * For visual commands, you should use {@link #relayCmd} instead.
21097      * <b>This should only be called after the editor is initialized.</b>
21098      * @param {String} cmd The Midas command
21099      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21100      */
21101     execCmd : function(cmd, value){
21102         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21103         this.syncValue();
21104     },
21105  
21106  
21107    
21108     /**
21109      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21110      * to insert tRoo.
21111      * @param {String} text | dom node.. 
21112      */
21113     insertAtCursor : function(text)
21114     {
21115         
21116         if(!this.activated){
21117             return;
21118         }
21119         /*
21120         if(Roo.isIE){
21121             this.win.focus();
21122             var r = this.doc.selection.createRange();
21123             if(r){
21124                 r.collapse(true);
21125                 r.pasteHTML(text);
21126                 this.syncValue();
21127                 this.deferFocus();
21128             
21129             }
21130             return;
21131         }
21132         */
21133         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21134             this.win.focus();
21135             
21136             
21137             // from jquery ui (MIT licenced)
21138             var range, node;
21139             var win = this.win;
21140             
21141             if (win.getSelection && win.getSelection().getRangeAt) {
21142                 range = win.getSelection().getRangeAt(0);
21143                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21144                 range.insertNode(node);
21145             } else if (win.document.selection && win.document.selection.createRange) {
21146                 // no firefox support
21147                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21148                 win.document.selection.createRange().pasteHTML(txt);
21149             } else {
21150                 // no firefox support
21151                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21152                 this.execCmd('InsertHTML', txt);
21153             } 
21154             
21155             this.syncValue();
21156             
21157             this.deferFocus();
21158         }
21159     },
21160  // private
21161     mozKeyPress : function(e){
21162         if(e.ctrlKey){
21163             var c = e.getCharCode(), cmd;
21164           
21165             if(c > 0){
21166                 c = String.fromCharCode(c).toLowerCase();
21167                 switch(c){
21168                     case 'b':
21169                         cmd = 'bold';
21170                         break;
21171                     case 'i':
21172                         cmd = 'italic';
21173                         break;
21174                     
21175                     case 'u':
21176                         cmd = 'underline';
21177                         break;
21178                     
21179                     case 'v':
21180                         this.cleanUpPaste.defer(100, this);
21181                         return;
21182                         
21183                 }
21184                 if(cmd){
21185                     this.win.focus();
21186                     this.execCmd(cmd);
21187                     this.deferFocus();
21188                     e.preventDefault();
21189                 }
21190                 
21191             }
21192         }
21193     },
21194
21195     // private
21196     fixKeys : function(){ // load time branching for fastest keydown performance
21197         if(Roo.isIE){
21198             return function(e){
21199                 var k = e.getKey(), r;
21200                 if(k == e.TAB){
21201                     e.stopEvent();
21202                     r = this.doc.selection.createRange();
21203                     if(r){
21204                         r.collapse(true);
21205                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21206                         this.deferFocus();
21207                     }
21208                     return;
21209                 }
21210                 
21211                 if(k == e.ENTER){
21212                     r = this.doc.selection.createRange();
21213                     if(r){
21214                         var target = r.parentElement();
21215                         if(!target || target.tagName.toLowerCase() != 'li'){
21216                             e.stopEvent();
21217                             r.pasteHTML('<br />');
21218                             r.collapse(false);
21219                             r.select();
21220                         }
21221                     }
21222                 }
21223                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21224                     this.cleanUpPaste.defer(100, this);
21225                     return;
21226                 }
21227                 
21228                 
21229             };
21230         }else if(Roo.isOpera){
21231             return function(e){
21232                 var k = e.getKey();
21233                 if(k == e.TAB){
21234                     e.stopEvent();
21235                     this.win.focus();
21236                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21237                     this.deferFocus();
21238                 }
21239                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21240                     this.cleanUpPaste.defer(100, this);
21241                     return;
21242                 }
21243                 
21244             };
21245         }else if(Roo.isSafari){
21246             return function(e){
21247                 var k = e.getKey();
21248                 
21249                 if(k == e.TAB){
21250                     e.stopEvent();
21251                     this.execCmd('InsertText','\t');
21252                     this.deferFocus();
21253                     return;
21254                 }
21255                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21256                     this.cleanUpPaste.defer(100, this);
21257                     return;
21258                 }
21259                 
21260              };
21261         }
21262     }(),
21263     
21264     getAllAncestors: function()
21265     {
21266         var p = this.getSelectedNode();
21267         var a = [];
21268         if (!p) {
21269             a.push(p); // push blank onto stack..
21270             p = this.getParentElement();
21271         }
21272         
21273         
21274         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21275             a.push(p);
21276             p = p.parentNode;
21277         }
21278         a.push(this.doc.body);
21279         return a;
21280     },
21281     lastSel : false,
21282     lastSelNode : false,
21283     
21284     
21285     getSelection : function() 
21286     {
21287         this.assignDocWin();
21288         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21289     },
21290     
21291     getSelectedNode: function() 
21292     {
21293         // this may only work on Gecko!!!
21294         
21295         // should we cache this!!!!
21296         
21297         
21298         
21299          
21300         var range = this.createRange(this.getSelection()).cloneRange();
21301         
21302         if (Roo.isIE) {
21303             var parent = range.parentElement();
21304             while (true) {
21305                 var testRange = range.duplicate();
21306                 testRange.moveToElementText(parent);
21307                 if (testRange.inRange(range)) {
21308                     break;
21309                 }
21310                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21311                     break;
21312                 }
21313                 parent = parent.parentElement;
21314             }
21315             return parent;
21316         }
21317         
21318         // is ancestor a text element.
21319         var ac =  range.commonAncestorContainer;
21320         if (ac.nodeType == 3) {
21321             ac = ac.parentNode;
21322         }
21323         
21324         var ar = ac.childNodes;
21325          
21326         var nodes = [];
21327         var other_nodes = [];
21328         var has_other_nodes = false;
21329         for (var i=0;i<ar.length;i++) {
21330             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21331                 continue;
21332             }
21333             // fullly contained node.
21334             
21335             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21336                 nodes.push(ar[i]);
21337                 continue;
21338             }
21339             
21340             // probably selected..
21341             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21342                 other_nodes.push(ar[i]);
21343                 continue;
21344             }
21345             // outer..
21346             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21347                 continue;
21348             }
21349             
21350             
21351             has_other_nodes = true;
21352         }
21353         if (!nodes.length && other_nodes.length) {
21354             nodes= other_nodes;
21355         }
21356         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21357             return false;
21358         }
21359         
21360         return nodes[0];
21361     },
21362     createRange: function(sel)
21363     {
21364         // this has strange effects when using with 
21365         // top toolbar - not sure if it's a great idea.
21366         //this.editor.contentWindow.focus();
21367         if (typeof sel != "undefined") {
21368             try {
21369                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21370             } catch(e) {
21371                 return this.doc.createRange();
21372             }
21373         } else {
21374             return this.doc.createRange();
21375         }
21376     },
21377     getParentElement: function()
21378     {
21379         
21380         this.assignDocWin();
21381         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21382         
21383         var range = this.createRange(sel);
21384          
21385         try {
21386             var p = range.commonAncestorContainer;
21387             while (p.nodeType == 3) { // text node
21388                 p = p.parentNode;
21389             }
21390             return p;
21391         } catch (e) {
21392             return null;
21393         }
21394     
21395     },
21396     /***
21397      *
21398      * Range intersection.. the hard stuff...
21399      *  '-1' = before
21400      *  '0' = hits..
21401      *  '1' = after.
21402      *         [ -- selected range --- ]
21403      *   [fail]                        [fail]
21404      *
21405      *    basically..
21406      *      if end is before start or  hits it. fail.
21407      *      if start is after end or hits it fail.
21408      *
21409      *   if either hits (but other is outside. - then it's not 
21410      *   
21411      *    
21412      **/
21413     
21414     
21415     // @see http://www.thismuchiknow.co.uk/?p=64.
21416     rangeIntersectsNode : function(range, node)
21417     {
21418         var nodeRange = node.ownerDocument.createRange();
21419         try {
21420             nodeRange.selectNode(node);
21421         } catch (e) {
21422             nodeRange.selectNodeContents(node);
21423         }
21424     
21425         var rangeStartRange = range.cloneRange();
21426         rangeStartRange.collapse(true);
21427     
21428         var rangeEndRange = range.cloneRange();
21429         rangeEndRange.collapse(false);
21430     
21431         var nodeStartRange = nodeRange.cloneRange();
21432         nodeStartRange.collapse(true);
21433     
21434         var nodeEndRange = nodeRange.cloneRange();
21435         nodeEndRange.collapse(false);
21436     
21437         return rangeStartRange.compareBoundaryPoints(
21438                  Range.START_TO_START, nodeEndRange) == -1 &&
21439                rangeEndRange.compareBoundaryPoints(
21440                  Range.START_TO_START, nodeStartRange) == 1;
21441         
21442          
21443     },
21444     rangeCompareNode : function(range, node)
21445     {
21446         var nodeRange = node.ownerDocument.createRange();
21447         try {
21448             nodeRange.selectNode(node);
21449         } catch (e) {
21450             nodeRange.selectNodeContents(node);
21451         }
21452         
21453         
21454         range.collapse(true);
21455     
21456         nodeRange.collapse(true);
21457      
21458         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21459         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21460          
21461         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21462         
21463         var nodeIsBefore   =  ss == 1;
21464         var nodeIsAfter    = ee == -1;
21465         
21466         if (nodeIsBefore && nodeIsAfter) {
21467             return 0; // outer
21468         }
21469         if (!nodeIsBefore && nodeIsAfter) {
21470             return 1; //right trailed.
21471         }
21472         
21473         if (nodeIsBefore && !nodeIsAfter) {
21474             return 2;  // left trailed.
21475         }
21476         // fully contined.
21477         return 3;
21478     },
21479
21480     // private? - in a new class?
21481     cleanUpPaste :  function()
21482     {
21483         // cleans up the whole document..
21484         Roo.log('cleanuppaste');
21485         
21486         this.cleanUpChildren(this.doc.body);
21487         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21488         if (clean != this.doc.body.innerHTML) {
21489             this.doc.body.innerHTML = clean;
21490         }
21491         
21492     },
21493     
21494     cleanWordChars : function(input) {// change the chars to hex code
21495         var he = Roo.HtmlEditorCore;
21496         
21497         var output = input;
21498         Roo.each(he.swapCodes, function(sw) { 
21499             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21500             
21501             output = output.replace(swapper, sw[1]);
21502         });
21503         
21504         return output;
21505     },
21506     
21507     
21508     cleanUpChildren : function (n)
21509     {
21510         if (!n.childNodes.length) {
21511             return;
21512         }
21513         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21514            this.cleanUpChild(n.childNodes[i]);
21515         }
21516     },
21517     
21518     
21519         
21520     
21521     cleanUpChild : function (node)
21522     {
21523         var ed = this;
21524         //console.log(node);
21525         if (node.nodeName == "#text") {
21526             // clean up silly Windows -- stuff?
21527             return; 
21528         }
21529         if (node.nodeName == "#comment" && !this.allowComments) {
21530             node.parentNode.removeChild(node);
21531             // clean up silly Windows -- stuff?
21532             return; 
21533         }
21534         var lcname = node.tagName.toLowerCase();
21535         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21536         // whitelist of tags..
21537         
21538         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21539             // remove node.
21540             node.parentNode.removeChild(node);
21541             return;
21542             
21543         }
21544         
21545         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21546         
21547         // spans with no attributes - just remove them..
21548         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21549             remove_keep_children = true;
21550         }
21551         
21552         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21553         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21554         
21555         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21556         //    remove_keep_children = true;
21557         //}
21558         
21559         if (remove_keep_children) {
21560             this.cleanUpChildren(node);
21561             // inserts everything just before this node...
21562             while (node.childNodes.length) {
21563                 var cn = node.childNodes[0];
21564                 node.removeChild(cn);
21565                 node.parentNode.insertBefore(cn, node);
21566             }
21567             node.parentNode.removeChild(node);
21568             return;
21569         }
21570         
21571         if (!node.attributes || !node.attributes.length) {
21572             
21573           
21574             
21575             
21576             this.cleanUpChildren(node);
21577             return;
21578         }
21579         
21580         function cleanAttr(n,v)
21581         {
21582             
21583             if (v.match(/^\./) || v.match(/^\//)) {
21584                 return;
21585             }
21586             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21587                 return;
21588             }
21589             if (v.match(/^#/)) {
21590                 return;
21591             }
21592             if (v.match(/^\{/)) { // allow template editing.
21593                 return;
21594             }
21595 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21596             node.removeAttribute(n);
21597             
21598         }
21599         
21600         var cwhite = this.cwhite;
21601         var cblack = this.cblack;
21602             
21603         function cleanStyle(n,v)
21604         {
21605             if (v.match(/expression/)) { //XSS?? should we even bother..
21606                 node.removeAttribute(n);
21607                 return;
21608             }
21609             
21610             var parts = v.split(/;/);
21611             var clean = [];
21612             
21613             Roo.each(parts, function(p) {
21614                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21615                 if (!p.length) {
21616                     return true;
21617                 }
21618                 var l = p.split(':').shift().replace(/\s+/g,'');
21619                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21620                 
21621                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21622 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21623                     //node.removeAttribute(n);
21624                     return true;
21625                 }
21626                 //Roo.log()
21627                 // only allow 'c whitelisted system attributes'
21628                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21629 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21630                     //node.removeAttribute(n);
21631                     return true;
21632                 }
21633                 
21634                 
21635                  
21636                 
21637                 clean.push(p);
21638                 return true;
21639             });
21640             if (clean.length) { 
21641                 node.setAttribute(n, clean.join(';'));
21642             } else {
21643                 node.removeAttribute(n);
21644             }
21645             
21646         }
21647         
21648         
21649         for (var i = node.attributes.length-1; i > -1 ; i--) {
21650             var a = node.attributes[i];
21651             //console.log(a);
21652             
21653             if (a.name.toLowerCase().substr(0,2)=='on')  {
21654                 node.removeAttribute(a.name);
21655                 continue;
21656             }
21657             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21658                 node.removeAttribute(a.name);
21659                 continue;
21660             }
21661             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21662                 cleanAttr(a.name,a.value); // fixme..
21663                 continue;
21664             }
21665             if (a.name == 'style') {
21666                 cleanStyle(a.name,a.value);
21667                 continue;
21668             }
21669             /// clean up MS crap..
21670             // tecnically this should be a list of valid class'es..
21671             
21672             
21673             if (a.name == 'class') {
21674                 if (a.value.match(/^Mso/)) {
21675                     node.removeAttribute('class');
21676                 }
21677                 
21678                 if (a.value.match(/^body$/)) {
21679                     node.removeAttribute('class');
21680                 }
21681                 continue;
21682             }
21683             
21684             // style cleanup!?
21685             // class cleanup?
21686             
21687         }
21688         
21689         
21690         this.cleanUpChildren(node);
21691         
21692         
21693     },
21694     
21695     /**
21696      * Clean up MS wordisms...
21697      */
21698     cleanWord : function(node)
21699     {
21700         if (!node) {
21701             this.cleanWord(this.doc.body);
21702             return;
21703         }
21704         
21705         if(
21706                 node.nodeName == 'SPAN' &&
21707                 !node.hasAttributes() &&
21708                 node.childNodes.length == 1 &&
21709                 node.firstChild.nodeName == "#text"  
21710         ) {
21711             var textNode = node.firstChild;
21712             node.removeChild(textNode);
21713             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21714                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21715             }
21716             node.parentNode.insertBefore(textNode, node);
21717             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21718                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21719             }
21720             node.parentNode.removeChild(node);
21721         }
21722         
21723         if (node.nodeName == "#text") {
21724             // clean up silly Windows -- stuff?
21725             return; 
21726         }
21727         if (node.nodeName == "#comment") {
21728             node.parentNode.removeChild(node);
21729             // clean up silly Windows -- stuff?
21730             return; 
21731         }
21732         
21733         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21734             node.parentNode.removeChild(node);
21735             return;
21736         }
21737         //Roo.log(node.tagName);
21738         // remove - but keep children..
21739         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21740             //Roo.log('-- removed');
21741             while (node.childNodes.length) {
21742                 var cn = node.childNodes[0];
21743                 node.removeChild(cn);
21744                 node.parentNode.insertBefore(cn, node);
21745                 // move node to parent - and clean it..
21746                 this.cleanWord(cn);
21747             }
21748             node.parentNode.removeChild(node);
21749             /// no need to iterate chidlren = it's got none..
21750             //this.iterateChildren(node, this.cleanWord);
21751             return;
21752         }
21753         // clean styles
21754         if (node.className.length) {
21755             
21756             var cn = node.className.split(/\W+/);
21757             var cna = [];
21758             Roo.each(cn, function(cls) {
21759                 if (cls.match(/Mso[a-zA-Z]+/)) {
21760                     return;
21761                 }
21762                 cna.push(cls);
21763             });
21764             node.className = cna.length ? cna.join(' ') : '';
21765             if (!cna.length) {
21766                 node.removeAttribute("class");
21767             }
21768         }
21769         
21770         if (node.hasAttribute("lang")) {
21771             node.removeAttribute("lang");
21772         }
21773         
21774         if (node.hasAttribute("style")) {
21775             
21776             var styles = node.getAttribute("style").split(";");
21777             var nstyle = [];
21778             Roo.each(styles, function(s) {
21779                 if (!s.match(/:/)) {
21780                     return;
21781                 }
21782                 var kv = s.split(":");
21783                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21784                     return;
21785                 }
21786                 // what ever is left... we allow.
21787                 nstyle.push(s);
21788             });
21789             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21790             if (!nstyle.length) {
21791                 node.removeAttribute('style');
21792             }
21793         }
21794         this.iterateChildren(node, this.cleanWord);
21795         
21796         
21797         
21798     },
21799     /**
21800      * iterateChildren of a Node, calling fn each time, using this as the scole..
21801      * @param {DomNode} node node to iterate children of.
21802      * @param {Function} fn method of this class to call on each item.
21803      */
21804     iterateChildren : function(node, fn)
21805     {
21806         if (!node.childNodes.length) {
21807                 return;
21808         }
21809         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21810            fn.call(this, node.childNodes[i])
21811         }
21812     },
21813     
21814     
21815     /**
21816      * cleanTableWidths.
21817      *
21818      * Quite often pasting from word etc.. results in tables with column and widths.
21819      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21820      *
21821      */
21822     cleanTableWidths : function(node)
21823     {
21824          
21825          
21826         if (!node) {
21827             this.cleanTableWidths(this.doc.body);
21828             return;
21829         }
21830         
21831         // ignore list...
21832         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21833             return; 
21834         }
21835         Roo.log(node.tagName);
21836         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21837             this.iterateChildren(node, this.cleanTableWidths);
21838             return;
21839         }
21840         if (node.hasAttribute('width')) {
21841             node.removeAttribute('width');
21842         }
21843         
21844          
21845         if (node.hasAttribute("style")) {
21846             // pretty basic...
21847             
21848             var styles = node.getAttribute("style").split(";");
21849             var nstyle = [];
21850             Roo.each(styles, function(s) {
21851                 if (!s.match(/:/)) {
21852                     return;
21853                 }
21854                 var kv = s.split(":");
21855                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21856                     return;
21857                 }
21858                 // what ever is left... we allow.
21859                 nstyle.push(s);
21860             });
21861             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21862             if (!nstyle.length) {
21863                 node.removeAttribute('style');
21864             }
21865         }
21866         
21867         this.iterateChildren(node, this.cleanTableWidths);
21868         
21869         
21870     },
21871     
21872     
21873     
21874     
21875     domToHTML : function(currentElement, depth, nopadtext) {
21876         
21877         depth = depth || 0;
21878         nopadtext = nopadtext || false;
21879     
21880         if (!currentElement) {
21881             return this.domToHTML(this.doc.body);
21882         }
21883         
21884         //Roo.log(currentElement);
21885         var j;
21886         var allText = false;
21887         var nodeName = currentElement.nodeName;
21888         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21889         
21890         if  (nodeName == '#text') {
21891             
21892             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21893         }
21894         
21895         
21896         var ret = '';
21897         if (nodeName != 'BODY') {
21898              
21899             var i = 0;
21900             // Prints the node tagName, such as <A>, <IMG>, etc
21901             if (tagName) {
21902                 var attr = [];
21903                 for(i = 0; i < currentElement.attributes.length;i++) {
21904                     // quoting?
21905                     var aname = currentElement.attributes.item(i).name;
21906                     if (!currentElement.attributes.item(i).value.length) {
21907                         continue;
21908                     }
21909                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21910                 }
21911                 
21912                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21913             } 
21914             else {
21915                 
21916                 // eack
21917             }
21918         } else {
21919             tagName = false;
21920         }
21921         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21922             return ret;
21923         }
21924         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21925             nopadtext = true;
21926         }
21927         
21928         
21929         // Traverse the tree
21930         i = 0;
21931         var currentElementChild = currentElement.childNodes.item(i);
21932         var allText = true;
21933         var innerHTML  = '';
21934         lastnode = '';
21935         while (currentElementChild) {
21936             // Formatting code (indent the tree so it looks nice on the screen)
21937             var nopad = nopadtext;
21938             if (lastnode == 'SPAN') {
21939                 nopad  = true;
21940             }
21941             // text
21942             if  (currentElementChild.nodeName == '#text') {
21943                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21944                 toadd = nopadtext ? toadd : toadd.trim();
21945                 if (!nopad && toadd.length > 80) {
21946                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21947                 }
21948                 innerHTML  += toadd;
21949                 
21950                 i++;
21951                 currentElementChild = currentElement.childNodes.item(i);
21952                 lastNode = '';
21953                 continue;
21954             }
21955             allText = false;
21956             
21957             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21958                 
21959             // Recursively traverse the tree structure of the child node
21960             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21961             lastnode = currentElementChild.nodeName;
21962             i++;
21963             currentElementChild=currentElement.childNodes.item(i);
21964         }
21965         
21966         ret += innerHTML;
21967         
21968         if (!allText) {
21969                 // The remaining code is mostly for formatting the tree
21970             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21971         }
21972         
21973         
21974         if (tagName) {
21975             ret+= "</"+tagName+">";
21976         }
21977         return ret;
21978         
21979     },
21980         
21981     applyBlacklists : function()
21982     {
21983         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21984         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21985         
21986         this.white = [];
21987         this.black = [];
21988         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21989             if (b.indexOf(tag) > -1) {
21990                 return;
21991             }
21992             this.white.push(tag);
21993             
21994         }, this);
21995         
21996         Roo.each(w, function(tag) {
21997             if (b.indexOf(tag) > -1) {
21998                 return;
21999             }
22000             if (this.white.indexOf(tag) > -1) {
22001                 return;
22002             }
22003             this.white.push(tag);
22004             
22005         }, this);
22006         
22007         
22008         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22009             if (w.indexOf(tag) > -1) {
22010                 return;
22011             }
22012             this.black.push(tag);
22013             
22014         }, this);
22015         
22016         Roo.each(b, function(tag) {
22017             if (w.indexOf(tag) > -1) {
22018                 return;
22019             }
22020             if (this.black.indexOf(tag) > -1) {
22021                 return;
22022             }
22023             this.black.push(tag);
22024             
22025         }, this);
22026         
22027         
22028         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22029         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22030         
22031         this.cwhite = [];
22032         this.cblack = [];
22033         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22034             if (b.indexOf(tag) > -1) {
22035                 return;
22036             }
22037             this.cwhite.push(tag);
22038             
22039         }, this);
22040         
22041         Roo.each(w, function(tag) {
22042             if (b.indexOf(tag) > -1) {
22043                 return;
22044             }
22045             if (this.cwhite.indexOf(tag) > -1) {
22046                 return;
22047             }
22048             this.cwhite.push(tag);
22049             
22050         }, this);
22051         
22052         
22053         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22054             if (w.indexOf(tag) > -1) {
22055                 return;
22056             }
22057             this.cblack.push(tag);
22058             
22059         }, this);
22060         
22061         Roo.each(b, function(tag) {
22062             if (w.indexOf(tag) > -1) {
22063                 return;
22064             }
22065             if (this.cblack.indexOf(tag) > -1) {
22066                 return;
22067             }
22068             this.cblack.push(tag);
22069             
22070         }, this);
22071     },
22072     
22073     setStylesheets : function(stylesheets)
22074     {
22075         if(typeof(stylesheets) == 'string'){
22076             Roo.get(this.iframe.contentDocument.head).createChild({
22077                 tag : 'link',
22078                 rel : 'stylesheet',
22079                 type : 'text/css',
22080                 href : stylesheets
22081             });
22082             
22083             return;
22084         }
22085         var _this = this;
22086      
22087         Roo.each(stylesheets, function(s) {
22088             if(!s.length){
22089                 return;
22090             }
22091             
22092             Roo.get(_this.iframe.contentDocument.head).createChild({
22093                 tag : 'link',
22094                 rel : 'stylesheet',
22095                 type : 'text/css',
22096                 href : s
22097             });
22098         });
22099
22100         
22101     },
22102     
22103     removeStylesheets : function()
22104     {
22105         var _this = this;
22106         
22107         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22108             s.remove();
22109         });
22110     },
22111     
22112     setStyle : function(style)
22113     {
22114         Roo.get(this.iframe.contentDocument.head).createChild({
22115             tag : 'style',
22116             type : 'text/css',
22117             html : style
22118         });
22119
22120         return;
22121     }
22122     
22123     // hide stuff that is not compatible
22124     /**
22125      * @event blur
22126      * @hide
22127      */
22128     /**
22129      * @event change
22130      * @hide
22131      */
22132     /**
22133      * @event focus
22134      * @hide
22135      */
22136     /**
22137      * @event specialkey
22138      * @hide
22139      */
22140     /**
22141      * @cfg {String} fieldClass @hide
22142      */
22143     /**
22144      * @cfg {String} focusClass @hide
22145      */
22146     /**
22147      * @cfg {String} autoCreate @hide
22148      */
22149     /**
22150      * @cfg {String} inputType @hide
22151      */
22152     /**
22153      * @cfg {String} invalidClass @hide
22154      */
22155     /**
22156      * @cfg {String} invalidText @hide
22157      */
22158     /**
22159      * @cfg {String} msgFx @hide
22160      */
22161     /**
22162      * @cfg {String} validateOnBlur @hide
22163      */
22164 });
22165
22166 Roo.HtmlEditorCore.white = [
22167         'area', 'br', 'img', 'input', 'hr', 'wbr',
22168         
22169        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22170        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22171        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22172        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22173        'table',   'ul',         'xmp', 
22174        
22175        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22176       'thead',   'tr', 
22177      
22178       'dir', 'menu', 'ol', 'ul', 'dl',
22179        
22180       'embed',  'object'
22181 ];
22182
22183
22184 Roo.HtmlEditorCore.black = [
22185     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22186         'applet', // 
22187         'base',   'basefont', 'bgsound', 'blink',  'body', 
22188         'frame',  'frameset', 'head',    'html',   'ilayer', 
22189         'iframe', 'layer',  'link',     'meta',    'object',   
22190         'script', 'style' ,'title',  'xml' // clean later..
22191 ];
22192 Roo.HtmlEditorCore.clean = [
22193     'script', 'style', 'title', 'xml'
22194 ];
22195 Roo.HtmlEditorCore.remove = [
22196     'font'
22197 ];
22198 // attributes..
22199
22200 Roo.HtmlEditorCore.ablack = [
22201     'on'
22202 ];
22203     
22204 Roo.HtmlEditorCore.aclean = [ 
22205     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22206 ];
22207
22208 // protocols..
22209 Roo.HtmlEditorCore.pwhite= [
22210         'http',  'https',  'mailto'
22211 ];
22212
22213 // white listed style attributes.
22214 Roo.HtmlEditorCore.cwhite= [
22215       //  'text-align', /// default is to allow most things..
22216       
22217          
22218 //        'font-size'//??
22219 ];
22220
22221 // black listed style attributes.
22222 Roo.HtmlEditorCore.cblack= [
22223       //  'font-size' -- this can be set by the project 
22224 ];
22225
22226
22227 Roo.HtmlEditorCore.swapCodes   =[ 
22228     [    8211, "&#8211;" ], 
22229     [    8212, "&#8212;" ], 
22230     [    8216,  "'" ],  
22231     [    8217, "'" ],  
22232     [    8220, '"' ],  
22233     [    8221, '"' ],  
22234     [    8226, "*" ],  
22235     [    8230, "..." ]
22236 ]; 
22237
22238     //<script type="text/javascript">
22239
22240 /*
22241  * Ext JS Library 1.1.1
22242  * Copyright(c) 2006-2007, Ext JS, LLC.
22243  * Licence LGPL
22244  * 
22245  */
22246  
22247  
22248 Roo.form.HtmlEditor = function(config){
22249     
22250     
22251     
22252     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22253     
22254     if (!this.toolbars) {
22255         this.toolbars = [];
22256     }
22257     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22258     
22259     
22260 };
22261
22262 /**
22263  * @class Roo.form.HtmlEditor
22264  * @extends Roo.form.Field
22265  * Provides a lightweight HTML Editor component.
22266  *
22267  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22268  * 
22269  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22270  * supported by this editor.</b><br/><br/>
22271  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22272  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22273  */
22274 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22275     /**
22276      * @cfg {Boolean} clearUp
22277      */
22278     clearUp : true,
22279       /**
22280      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22281      */
22282     toolbars : false,
22283    
22284      /**
22285      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22286      *                        Roo.resizable.
22287      */
22288     resizable : false,
22289      /**
22290      * @cfg {Number} height (in pixels)
22291      */   
22292     height: 300,
22293    /**
22294      * @cfg {Number} width (in pixels)
22295      */   
22296     width: 500,
22297     
22298     /**
22299      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22300      * 
22301      */
22302     stylesheets: false,
22303     
22304     
22305      /**
22306      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22307      * 
22308      */
22309     cblack: false,
22310     /**
22311      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22312      * 
22313      */
22314     cwhite: false,
22315     
22316      /**
22317      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22318      * 
22319      */
22320     black: false,
22321     /**
22322      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22323      * 
22324      */
22325     white: false,
22326     /**
22327      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22328      */
22329     allowComments: false,
22330     
22331     // id of frame..
22332     frameId: false,
22333     
22334     // private properties
22335     validationEvent : false,
22336     deferHeight: true,
22337     initialized : false,
22338     activated : false,
22339     
22340     onFocus : Roo.emptyFn,
22341     iframePad:3,
22342     hideMode:'offsets',
22343     
22344     actionMode : 'container', // defaults to hiding it...
22345     
22346     defaultAutoCreate : { // modified by initCompnoent..
22347         tag: "textarea",
22348         style:"width:500px;height:300px;",
22349         autocomplete: "new-password"
22350     },
22351
22352     // private
22353     initComponent : function(){
22354         this.addEvents({
22355             /**
22356              * @event initialize
22357              * Fires when the editor is fully initialized (including the iframe)
22358              * @param {HtmlEditor} this
22359              */
22360             initialize: true,
22361             /**
22362              * @event activate
22363              * Fires when the editor is first receives the focus. Any insertion must wait
22364              * until after this event.
22365              * @param {HtmlEditor} this
22366              */
22367             activate: true,
22368              /**
22369              * @event beforesync
22370              * Fires before the textarea is updated with content from the editor iframe. Return false
22371              * to cancel the sync.
22372              * @param {HtmlEditor} this
22373              * @param {String} html
22374              */
22375             beforesync: true,
22376              /**
22377              * @event beforepush
22378              * Fires before the iframe editor is updated with content from the textarea. Return false
22379              * to cancel the push.
22380              * @param {HtmlEditor} this
22381              * @param {String} html
22382              */
22383             beforepush: true,
22384              /**
22385              * @event sync
22386              * Fires when the textarea is updated with content from the editor iframe.
22387              * @param {HtmlEditor} this
22388              * @param {String} html
22389              */
22390             sync: true,
22391              /**
22392              * @event push
22393              * Fires when the iframe editor is updated with content from the textarea.
22394              * @param {HtmlEditor} this
22395              * @param {String} html
22396              */
22397             push: true,
22398              /**
22399              * @event editmodechange
22400              * Fires when the editor switches edit modes
22401              * @param {HtmlEditor} this
22402              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22403              */
22404             editmodechange: true,
22405             /**
22406              * @event editorevent
22407              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22408              * @param {HtmlEditor} this
22409              */
22410             editorevent: true,
22411             /**
22412              * @event firstfocus
22413              * Fires when on first focus - needed by toolbars..
22414              * @param {HtmlEditor} this
22415              */
22416             firstfocus: true,
22417             /**
22418              * @event autosave
22419              * Auto save the htmlEditor value as a file into Events
22420              * @param {HtmlEditor} this
22421              */
22422             autosave: true,
22423             /**
22424              * @event savedpreview
22425              * preview the saved version of htmlEditor
22426              * @param {HtmlEditor} this
22427              */
22428             savedpreview: true,
22429             
22430             /**
22431             * @event stylesheetsclick
22432             * Fires when press the Sytlesheets button
22433             * @param {Roo.HtmlEditorCore} this
22434             */
22435             stylesheetsclick: true
22436         });
22437         this.defaultAutoCreate =  {
22438             tag: "textarea",
22439             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22440             autocomplete: "new-password"
22441         };
22442     },
22443
22444     /**
22445      * Protected method that will not generally be called directly. It
22446      * is called when the editor creates its toolbar. Override this method if you need to
22447      * add custom toolbar buttons.
22448      * @param {HtmlEditor} editor
22449      */
22450     createToolbar : function(editor){
22451         Roo.log("create toolbars");
22452         if (!editor.toolbars || !editor.toolbars.length) {
22453             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22454         }
22455         
22456         for (var i =0 ; i < editor.toolbars.length;i++) {
22457             editor.toolbars[i] = Roo.factory(
22458                     typeof(editor.toolbars[i]) == 'string' ?
22459                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22460                 Roo.form.HtmlEditor);
22461             editor.toolbars[i].init(editor);
22462         }
22463          
22464         
22465     },
22466
22467      
22468     // private
22469     onRender : function(ct, position)
22470     {
22471         var _t = this;
22472         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22473         
22474         this.wrap = this.el.wrap({
22475             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22476         });
22477         
22478         this.editorcore.onRender(ct, position);
22479          
22480         if (this.resizable) {
22481             this.resizeEl = new Roo.Resizable(this.wrap, {
22482                 pinned : true,
22483                 wrap: true,
22484                 dynamic : true,
22485                 minHeight : this.height,
22486                 height: this.height,
22487                 handles : this.resizable,
22488                 width: this.width,
22489                 listeners : {
22490                     resize : function(r, w, h) {
22491                         _t.onResize(w,h); // -something
22492                     }
22493                 }
22494             });
22495             
22496         }
22497         this.createToolbar(this);
22498        
22499         
22500         if(!this.width){
22501             this.setSize(this.wrap.getSize());
22502         }
22503         if (this.resizeEl) {
22504             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22505             // should trigger onReize..
22506         }
22507         
22508         this.keyNav = new Roo.KeyNav(this.el, {
22509             
22510             "tab" : function(e){
22511                 e.preventDefault();
22512                 
22513                 var value = this.getValue();
22514                 
22515                 var start = this.el.dom.selectionStart;
22516                 var end = this.el.dom.selectionEnd;
22517                 
22518                 if(!e.shiftKey){
22519                     
22520                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22521                     this.el.dom.setSelectionRange(end + 1, end + 1);
22522                     return;
22523                 }
22524                 
22525                 var f = value.substring(0, start).split("\t");
22526                 
22527                 if(f.pop().length != 0){
22528                     return;
22529                 }
22530                 
22531                 this.setValue(f.join("\t") + value.substring(end));
22532                 this.el.dom.setSelectionRange(start - 1, start - 1);
22533                 
22534             },
22535             
22536             "home" : function(e){
22537                 e.preventDefault();
22538                 
22539                 var curr = this.el.dom.selectionStart;
22540                 var lines = this.getValue().split("\n");
22541                 
22542                 if(!lines.length){
22543                     return;
22544                 }
22545                 
22546                 if(e.ctrlKey){
22547                     this.el.dom.setSelectionRange(0, 0);
22548                     return;
22549                 }
22550                 
22551                 var pos = 0;
22552                 
22553                 for (var i = 0; i < lines.length;i++) {
22554                     pos += lines[i].length;
22555                     
22556                     if(i != 0){
22557                         pos += 1;
22558                     }
22559                     
22560                     if(pos < curr){
22561                         continue;
22562                     }
22563                     
22564                     pos -= lines[i].length;
22565                     
22566                     break;
22567                 }
22568                 
22569                 if(!e.shiftKey){
22570                     this.el.dom.setSelectionRange(pos, pos);
22571                     return;
22572                 }
22573                 
22574                 this.el.dom.selectionStart = pos;
22575                 this.el.dom.selectionEnd = curr;
22576             },
22577             
22578             "end" : function(e){
22579                 e.preventDefault();
22580                 
22581                 var curr = this.el.dom.selectionStart;
22582                 var lines = this.getValue().split("\n");
22583                 
22584                 if(!lines.length){
22585                     return;
22586                 }
22587                 
22588                 if(e.ctrlKey){
22589                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22590                     return;
22591                 }
22592                 
22593                 var pos = 0;
22594                 
22595                 for (var i = 0; i < lines.length;i++) {
22596                     
22597                     pos += lines[i].length;
22598                     
22599                     if(i != 0){
22600                         pos += 1;
22601                     }
22602                     
22603                     if(pos < curr){
22604                         continue;
22605                     }
22606                     
22607                     break;
22608                 }
22609                 
22610                 if(!e.shiftKey){
22611                     this.el.dom.setSelectionRange(pos, pos);
22612                     return;
22613                 }
22614                 
22615                 this.el.dom.selectionStart = curr;
22616                 this.el.dom.selectionEnd = pos;
22617             },
22618
22619             scope : this,
22620
22621             doRelay : function(foo, bar, hname){
22622                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22623             },
22624
22625             forceKeyDown: true
22626         });
22627         
22628 //        if(this.autosave && this.w){
22629 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22630 //        }
22631     },
22632
22633     // private
22634     onResize : function(w, h)
22635     {
22636         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22637         var ew = false;
22638         var eh = false;
22639         
22640         if(this.el ){
22641             if(typeof w == 'number'){
22642                 var aw = w - this.wrap.getFrameWidth('lr');
22643                 this.el.setWidth(this.adjustWidth('textarea', aw));
22644                 ew = aw;
22645             }
22646             if(typeof h == 'number'){
22647                 var tbh = 0;
22648                 for (var i =0; i < this.toolbars.length;i++) {
22649                     // fixme - ask toolbars for heights?
22650                     tbh += this.toolbars[i].tb.el.getHeight();
22651                     if (this.toolbars[i].footer) {
22652                         tbh += this.toolbars[i].footer.el.getHeight();
22653                     }
22654                 }
22655                 
22656                 
22657                 
22658                 
22659                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22660                 ah -= 5; // knock a few pixes off for look..
22661 //                Roo.log(ah);
22662                 this.el.setHeight(this.adjustWidth('textarea', ah));
22663                 var eh = ah;
22664             }
22665         }
22666         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22667         this.editorcore.onResize(ew,eh);
22668         
22669     },
22670
22671     /**
22672      * Toggles the editor between standard and source edit mode.
22673      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22674      */
22675     toggleSourceEdit : function(sourceEditMode)
22676     {
22677         this.editorcore.toggleSourceEdit(sourceEditMode);
22678         
22679         if(this.editorcore.sourceEditMode){
22680             Roo.log('editor - showing textarea');
22681             
22682 //            Roo.log('in');
22683 //            Roo.log(this.syncValue());
22684             this.editorcore.syncValue();
22685             this.el.removeClass('x-hidden');
22686             this.el.dom.removeAttribute('tabIndex');
22687             this.el.focus();
22688             
22689             for (var i = 0; i < this.toolbars.length; i++) {
22690                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22691                     this.toolbars[i].tb.hide();
22692                     this.toolbars[i].footer.hide();
22693                 }
22694             }
22695             
22696         }else{
22697             Roo.log('editor - hiding textarea');
22698 //            Roo.log('out')
22699 //            Roo.log(this.pushValue()); 
22700             this.editorcore.pushValue();
22701             
22702             this.el.addClass('x-hidden');
22703             this.el.dom.setAttribute('tabIndex', -1);
22704             
22705             for (var i = 0; i < this.toolbars.length; i++) {
22706                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22707                     this.toolbars[i].tb.show();
22708                     this.toolbars[i].footer.show();
22709                 }
22710             }
22711             
22712             //this.deferFocus();
22713         }
22714         
22715         this.setSize(this.wrap.getSize());
22716         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22717         
22718         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22719     },
22720  
22721     // private (for BoxComponent)
22722     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22723
22724     // private (for BoxComponent)
22725     getResizeEl : function(){
22726         return this.wrap;
22727     },
22728
22729     // private (for BoxComponent)
22730     getPositionEl : function(){
22731         return this.wrap;
22732     },
22733
22734     // private
22735     initEvents : function(){
22736         this.originalValue = this.getValue();
22737     },
22738
22739     /**
22740      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22741      * @method
22742      */
22743     markInvalid : Roo.emptyFn,
22744     /**
22745      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22746      * @method
22747      */
22748     clearInvalid : Roo.emptyFn,
22749
22750     setValue : function(v){
22751         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22752         this.editorcore.pushValue();
22753     },
22754
22755      
22756     // private
22757     deferFocus : function(){
22758         this.focus.defer(10, this);
22759     },
22760
22761     // doc'ed in Field
22762     focus : function(){
22763         this.editorcore.focus();
22764         
22765     },
22766       
22767
22768     // private
22769     onDestroy : function(){
22770         
22771         
22772         
22773         if(this.rendered){
22774             
22775             for (var i =0; i < this.toolbars.length;i++) {
22776                 // fixme - ask toolbars for heights?
22777                 this.toolbars[i].onDestroy();
22778             }
22779             
22780             this.wrap.dom.innerHTML = '';
22781             this.wrap.remove();
22782         }
22783     },
22784
22785     // private
22786     onFirstFocus : function(){
22787         //Roo.log("onFirstFocus");
22788         this.editorcore.onFirstFocus();
22789          for (var i =0; i < this.toolbars.length;i++) {
22790             this.toolbars[i].onFirstFocus();
22791         }
22792         
22793     },
22794     
22795     // private
22796     syncValue : function()
22797     {
22798         this.editorcore.syncValue();
22799     },
22800     
22801     pushValue : function()
22802     {
22803         this.editorcore.pushValue();
22804     },
22805     
22806     setStylesheets : function(stylesheets)
22807     {
22808         this.editorcore.setStylesheets(stylesheets);
22809     },
22810     
22811     removeStylesheets : function()
22812     {
22813         this.editorcore.removeStylesheets();
22814     }
22815      
22816     
22817     // hide stuff that is not compatible
22818     /**
22819      * @event blur
22820      * @hide
22821      */
22822     /**
22823      * @event change
22824      * @hide
22825      */
22826     /**
22827      * @event focus
22828      * @hide
22829      */
22830     /**
22831      * @event specialkey
22832      * @hide
22833      */
22834     /**
22835      * @cfg {String} fieldClass @hide
22836      */
22837     /**
22838      * @cfg {String} focusClass @hide
22839      */
22840     /**
22841      * @cfg {String} autoCreate @hide
22842      */
22843     /**
22844      * @cfg {String} inputType @hide
22845      */
22846     /**
22847      * @cfg {String} invalidClass @hide
22848      */
22849     /**
22850      * @cfg {String} invalidText @hide
22851      */
22852     /**
22853      * @cfg {String} msgFx @hide
22854      */
22855     /**
22856      * @cfg {String} validateOnBlur @hide
22857      */
22858 });
22859  
22860     // <script type="text/javascript">
22861 /*
22862  * Based on
22863  * Ext JS Library 1.1.1
22864  * Copyright(c) 2006-2007, Ext JS, LLC.
22865  *  
22866  
22867  */
22868
22869 /**
22870  * @class Roo.form.HtmlEditorToolbar1
22871  * Basic Toolbar
22872  * 
22873  * Usage:
22874  *
22875  new Roo.form.HtmlEditor({
22876     ....
22877     toolbars : [
22878         new Roo.form.HtmlEditorToolbar1({
22879             disable : { fonts: 1 , format: 1, ..., ... , ...],
22880             btns : [ .... ]
22881         })
22882     }
22883      
22884  * 
22885  * @cfg {Object} disable List of elements to disable..
22886  * @cfg {Array} btns List of additional buttons.
22887  * 
22888  * 
22889  * NEEDS Extra CSS? 
22890  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22891  */
22892  
22893 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22894 {
22895     
22896     Roo.apply(this, config);
22897     
22898     // default disabled, based on 'good practice'..
22899     this.disable = this.disable || {};
22900     Roo.applyIf(this.disable, {
22901         fontSize : true,
22902         colors : true,
22903         specialElements : true
22904     });
22905     
22906     
22907     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22908     // dont call parent... till later.
22909 }
22910
22911 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22912     
22913     tb: false,
22914     
22915     rendered: false,
22916     
22917     editor : false,
22918     editorcore : false,
22919     /**
22920      * @cfg {Object} disable  List of toolbar elements to disable
22921          
22922      */
22923     disable : false,
22924     
22925     
22926      /**
22927      * @cfg {String} createLinkText The default text for the create link prompt
22928      */
22929     createLinkText : 'Please enter the URL for the link:',
22930     /**
22931      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22932      */
22933     defaultLinkValue : 'http:/'+'/',
22934    
22935     
22936       /**
22937      * @cfg {Array} fontFamilies An array of available font families
22938      */
22939     fontFamilies : [
22940         'Arial',
22941         'Courier New',
22942         'Tahoma',
22943         'Times New Roman',
22944         'Verdana'
22945     ],
22946     
22947     specialChars : [
22948            "&#169;",
22949           "&#174;",     
22950           "&#8482;",    
22951           "&#163;" ,    
22952          // "&#8212;",    
22953           "&#8230;",    
22954           "&#247;" ,    
22955         //  "&#225;" ,     ?? a acute?
22956            "&#8364;"    , //Euro
22957        //   "&#8220;"    ,
22958         //  "&#8221;"    ,
22959         //  "&#8226;"    ,
22960           "&#176;"  //   , // degrees
22961
22962          // "&#233;"     , // e ecute
22963          // "&#250;"     , // u ecute?
22964     ],
22965     
22966     specialElements : [
22967         {
22968             text: "Insert Table",
22969             xtype: 'MenuItem',
22970             xns : Roo.Menu,
22971             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22972                 
22973         },
22974         {    
22975             text: "Insert Image",
22976             xtype: 'MenuItem',
22977             xns : Roo.Menu,
22978             ihtml : '<img src="about:blank"/>'
22979             
22980         }
22981         
22982          
22983     ],
22984     
22985     
22986     inputElements : [ 
22987             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22988             "input:submit", "input:button", "select", "textarea", "label" ],
22989     formats : [
22990         ["p"] ,  
22991         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22992         ["pre"],[ "code"], 
22993         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22994         ['div'],['span'],
22995         ['sup'],['sub']
22996     ],
22997     
22998     cleanStyles : [
22999         "font-size"
23000     ],
23001      /**
23002      * @cfg {String} defaultFont default font to use.
23003      */
23004     defaultFont: 'tahoma',
23005    
23006     fontSelect : false,
23007     
23008     
23009     formatCombo : false,
23010     
23011     init : function(editor)
23012     {
23013         this.editor = editor;
23014         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23015         var editorcore = this.editorcore;
23016         
23017         var _t = this;
23018         
23019         var fid = editorcore.frameId;
23020         var etb = this;
23021         function btn(id, toggle, handler){
23022             var xid = fid + '-'+ id ;
23023             return {
23024                 id : xid,
23025                 cmd : id,
23026                 cls : 'x-btn-icon x-edit-'+id,
23027                 enableToggle:toggle !== false,
23028                 scope: _t, // was editor...
23029                 handler:handler||_t.relayBtnCmd,
23030                 clickEvent:'mousedown',
23031                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23032                 tabIndex:-1
23033             };
23034         }
23035         
23036         
23037         
23038         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23039         this.tb = tb;
23040          // stop form submits
23041         tb.el.on('click', function(e){
23042             e.preventDefault(); // what does this do?
23043         });
23044
23045         if(!this.disable.font) { // && !Roo.isSafari){
23046             /* why no safari for fonts 
23047             editor.fontSelect = tb.el.createChild({
23048                 tag:'select',
23049                 tabIndex: -1,
23050                 cls:'x-font-select',
23051                 html: this.createFontOptions()
23052             });
23053             
23054             editor.fontSelect.on('change', function(){
23055                 var font = editor.fontSelect.dom.value;
23056                 editor.relayCmd('fontname', font);
23057                 editor.deferFocus();
23058             }, editor);
23059             
23060             tb.add(
23061                 editor.fontSelect.dom,
23062                 '-'
23063             );
23064             */
23065             
23066         };
23067         if(!this.disable.formats){
23068             this.formatCombo = new Roo.form.ComboBox({
23069                 store: new Roo.data.SimpleStore({
23070                     id : 'tag',
23071                     fields: ['tag'],
23072                     data : this.formats // from states.js
23073                 }),
23074                 blockFocus : true,
23075                 name : '',
23076                 //autoCreate : {tag: "div",  size: "20"},
23077                 displayField:'tag',
23078                 typeAhead: false,
23079                 mode: 'local',
23080                 editable : false,
23081                 triggerAction: 'all',
23082                 emptyText:'Add tag',
23083                 selectOnFocus:true,
23084                 width:135,
23085                 listeners : {
23086                     'select': function(c, r, i) {
23087                         editorcore.insertTag(r.get('tag'));
23088                         editor.focus();
23089                     }
23090                 }
23091
23092             });
23093             tb.addField(this.formatCombo);
23094             
23095         }
23096         
23097         if(!this.disable.format){
23098             tb.add(
23099                 btn('bold'),
23100                 btn('italic'),
23101                 btn('underline'),
23102                 btn('strikethrough')
23103             );
23104         };
23105         if(!this.disable.fontSize){
23106             tb.add(
23107                 '-',
23108                 
23109                 
23110                 btn('increasefontsize', false, editorcore.adjustFont),
23111                 btn('decreasefontsize', false, editorcore.adjustFont)
23112             );
23113         };
23114         
23115         
23116         if(!this.disable.colors){
23117             tb.add(
23118                 '-', {
23119                     id:editorcore.frameId +'-forecolor',
23120                     cls:'x-btn-icon x-edit-forecolor',
23121                     clickEvent:'mousedown',
23122                     tooltip: this.buttonTips['forecolor'] || undefined,
23123                     tabIndex:-1,
23124                     menu : new Roo.menu.ColorMenu({
23125                         allowReselect: true,
23126                         focus: Roo.emptyFn,
23127                         value:'000000',
23128                         plain:true,
23129                         selectHandler: function(cp, color){
23130                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23131                             editor.deferFocus();
23132                         },
23133                         scope: editorcore,
23134                         clickEvent:'mousedown'
23135                     })
23136                 }, {
23137                     id:editorcore.frameId +'backcolor',
23138                     cls:'x-btn-icon x-edit-backcolor',
23139                     clickEvent:'mousedown',
23140                     tooltip: this.buttonTips['backcolor'] || undefined,
23141                     tabIndex:-1,
23142                     menu : new Roo.menu.ColorMenu({
23143                         focus: Roo.emptyFn,
23144                         value:'FFFFFF',
23145                         plain:true,
23146                         allowReselect: true,
23147                         selectHandler: function(cp, color){
23148                             if(Roo.isGecko){
23149                                 editorcore.execCmd('useCSS', false);
23150                                 editorcore.execCmd('hilitecolor', color);
23151                                 editorcore.execCmd('useCSS', true);
23152                                 editor.deferFocus();
23153                             }else{
23154                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23155                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23156                                 editor.deferFocus();
23157                             }
23158                         },
23159                         scope:editorcore,
23160                         clickEvent:'mousedown'
23161                     })
23162                 }
23163             );
23164         };
23165         // now add all the items...
23166         
23167
23168         if(!this.disable.alignments){
23169             tb.add(
23170                 '-',
23171                 btn('justifyleft'),
23172                 btn('justifycenter'),
23173                 btn('justifyright')
23174             );
23175         };
23176
23177         //if(!Roo.isSafari){
23178             if(!this.disable.links){
23179                 tb.add(
23180                     '-',
23181                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23182                 );
23183             };
23184
23185             if(!this.disable.lists){
23186                 tb.add(
23187                     '-',
23188                     btn('insertorderedlist'),
23189                     btn('insertunorderedlist')
23190                 );
23191             }
23192             if(!this.disable.sourceEdit){
23193                 tb.add(
23194                     '-',
23195                     btn('sourceedit', true, function(btn){
23196                         this.toggleSourceEdit(btn.pressed);
23197                     })
23198                 );
23199             }
23200         //}
23201         
23202         var smenu = { };
23203         // special menu.. - needs to be tidied up..
23204         if (!this.disable.special) {
23205             smenu = {
23206                 text: "&#169;",
23207                 cls: 'x-edit-none',
23208                 
23209                 menu : {
23210                     items : []
23211                 }
23212             };
23213             for (var i =0; i < this.specialChars.length; i++) {
23214                 smenu.menu.items.push({
23215                     
23216                     html: this.specialChars[i],
23217                     handler: function(a,b) {
23218                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23219                         //editor.insertAtCursor(a.html);
23220                         
23221                     },
23222                     tabIndex:-1
23223                 });
23224             }
23225             
23226             
23227             tb.add(smenu);
23228             
23229             
23230         }
23231         
23232         var cmenu = { };
23233         if (!this.disable.cleanStyles) {
23234             cmenu = {
23235                 cls: 'x-btn-icon x-btn-clear',
23236                 
23237                 menu : {
23238                     items : []
23239                 }
23240             };
23241             for (var i =0; i < this.cleanStyles.length; i++) {
23242                 cmenu.menu.items.push({
23243                     actiontype : this.cleanStyles[i],
23244                     html: 'Remove ' + this.cleanStyles[i],
23245                     handler: function(a,b) {
23246 //                        Roo.log(a);
23247 //                        Roo.log(b);
23248                         var c = Roo.get(editorcore.doc.body);
23249                         c.select('[style]').each(function(s) {
23250                             s.dom.style.removeProperty(a.actiontype);
23251                         });
23252                         editorcore.syncValue();
23253                     },
23254                     tabIndex:-1
23255                 });
23256             }
23257              cmenu.menu.items.push({
23258                 actiontype : 'tablewidths',
23259                 html: 'Remove Table Widths',
23260                 handler: function(a,b) {
23261                     editorcore.cleanTableWidths();
23262                     editorcore.syncValue();
23263                 },
23264                 tabIndex:-1
23265             });
23266             cmenu.menu.items.push({
23267                 actiontype : 'word',
23268                 html: 'Remove MS Word Formating',
23269                 handler: function(a,b) {
23270                     editorcore.cleanWord();
23271                     editorcore.syncValue();
23272                 },
23273                 tabIndex:-1
23274             });
23275             
23276             cmenu.menu.items.push({
23277                 actiontype : 'all',
23278                 html: 'Remove All Styles',
23279                 handler: function(a,b) {
23280                     
23281                     var c = Roo.get(editorcore.doc.body);
23282                     c.select('[style]').each(function(s) {
23283                         s.dom.removeAttribute('style');
23284                     });
23285                     editorcore.syncValue();
23286                 },
23287                 tabIndex:-1
23288             });
23289             
23290             cmenu.menu.items.push({
23291                 actiontype : 'all',
23292                 html: 'Remove All CSS Classes',
23293                 handler: function(a,b) {
23294                     
23295                     var c = Roo.get(editorcore.doc.body);
23296                     c.select('[class]').each(function(s) {
23297                         s.dom.removeAttribute('class');
23298                     });
23299                     editorcore.cleanWord();
23300                     editorcore.syncValue();
23301                 },
23302                 tabIndex:-1
23303             });
23304             
23305              cmenu.menu.items.push({
23306                 actiontype : 'tidy',
23307                 html: 'Tidy HTML Source',
23308                 handler: function(a,b) {
23309                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23310                     editorcore.syncValue();
23311                 },
23312                 tabIndex:-1
23313             });
23314             
23315             
23316             tb.add(cmenu);
23317         }
23318          
23319         if (!this.disable.specialElements) {
23320             var semenu = {
23321                 text: "Other;",
23322                 cls: 'x-edit-none',
23323                 menu : {
23324                     items : []
23325                 }
23326             };
23327             for (var i =0; i < this.specialElements.length; i++) {
23328                 semenu.menu.items.push(
23329                     Roo.apply({ 
23330                         handler: function(a,b) {
23331                             editor.insertAtCursor(this.ihtml);
23332                         }
23333                     }, this.specialElements[i])
23334                 );
23335                     
23336             }
23337             
23338             tb.add(semenu);
23339             
23340             
23341         }
23342          
23343         
23344         if (this.btns) {
23345             for(var i =0; i< this.btns.length;i++) {
23346                 var b = Roo.factory(this.btns[i],Roo.form);
23347                 b.cls =  'x-edit-none';
23348                 
23349                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23350                     b.cls += ' x-init-enable';
23351                 }
23352                 
23353                 b.scope = editorcore;
23354                 tb.add(b);
23355             }
23356         
23357         }
23358         
23359         
23360         
23361         // disable everything...
23362         
23363         this.tb.items.each(function(item){
23364             
23365            if(
23366                 item.id != editorcore.frameId+ '-sourceedit' && 
23367                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23368             ){
23369                 
23370                 item.disable();
23371             }
23372         });
23373         this.rendered = true;
23374         
23375         // the all the btns;
23376         editor.on('editorevent', this.updateToolbar, this);
23377         // other toolbars need to implement this..
23378         //editor.on('editmodechange', this.updateToolbar, this);
23379     },
23380     
23381     
23382     relayBtnCmd : function(btn) {
23383         this.editorcore.relayCmd(btn.cmd);
23384     },
23385     // private used internally
23386     createLink : function(){
23387         Roo.log("create link?");
23388         var url = prompt(this.createLinkText, this.defaultLinkValue);
23389         if(url && url != 'http:/'+'/'){
23390             this.editorcore.relayCmd('createlink', url);
23391         }
23392     },
23393
23394     
23395     /**
23396      * Protected method that will not generally be called directly. It triggers
23397      * a toolbar update by reading the markup state of the current selection in the editor.
23398      */
23399     updateToolbar: function(){
23400
23401         if(!this.editorcore.activated){
23402             this.editor.onFirstFocus();
23403             return;
23404         }
23405
23406         var btns = this.tb.items.map, 
23407             doc = this.editorcore.doc,
23408             frameId = this.editorcore.frameId;
23409
23410         if(!this.disable.font && !Roo.isSafari){
23411             /*
23412             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23413             if(name != this.fontSelect.dom.value){
23414                 this.fontSelect.dom.value = name;
23415             }
23416             */
23417         }
23418         if(!this.disable.format){
23419             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23420             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23421             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23422             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23423         }
23424         if(!this.disable.alignments){
23425             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23426             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23427             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23428         }
23429         if(!Roo.isSafari && !this.disable.lists){
23430             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23431             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23432         }
23433         
23434         var ans = this.editorcore.getAllAncestors();
23435         if (this.formatCombo) {
23436             
23437             
23438             var store = this.formatCombo.store;
23439             this.formatCombo.setValue("");
23440             for (var i =0; i < ans.length;i++) {
23441                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23442                     // select it..
23443                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23444                     break;
23445                 }
23446             }
23447         }
23448         
23449         
23450         
23451         // hides menus... - so this cant be on a menu...
23452         Roo.menu.MenuMgr.hideAll();
23453
23454         //this.editorsyncValue();
23455     },
23456    
23457     
23458     createFontOptions : function(){
23459         var buf = [], fs = this.fontFamilies, ff, lc;
23460         
23461         
23462         
23463         for(var i = 0, len = fs.length; i< len; i++){
23464             ff = fs[i];
23465             lc = ff.toLowerCase();
23466             buf.push(
23467                 '<option value="',lc,'" style="font-family:',ff,';"',
23468                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23469                     ff,
23470                 '</option>'
23471             );
23472         }
23473         return buf.join('');
23474     },
23475     
23476     toggleSourceEdit : function(sourceEditMode){
23477         
23478         Roo.log("toolbar toogle");
23479         if(sourceEditMode === undefined){
23480             sourceEditMode = !this.sourceEditMode;
23481         }
23482         this.sourceEditMode = sourceEditMode === true;
23483         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23484         // just toggle the button?
23485         if(btn.pressed !== this.sourceEditMode){
23486             btn.toggle(this.sourceEditMode);
23487             return;
23488         }
23489         
23490         if(sourceEditMode){
23491             Roo.log("disabling buttons");
23492             this.tb.items.each(function(item){
23493                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23494                     item.disable();
23495                 }
23496             });
23497           
23498         }else{
23499             Roo.log("enabling buttons");
23500             if(this.editorcore.initialized){
23501                 this.tb.items.each(function(item){
23502                     item.enable();
23503                 });
23504             }
23505             
23506         }
23507         Roo.log("calling toggole on editor");
23508         // tell the editor that it's been pressed..
23509         this.editor.toggleSourceEdit(sourceEditMode);
23510        
23511     },
23512      /**
23513      * Object collection of toolbar tooltips for the buttons in the editor. The key
23514      * is the command id associated with that button and the value is a valid QuickTips object.
23515      * For example:
23516 <pre><code>
23517 {
23518     bold : {
23519         title: 'Bold (Ctrl+B)',
23520         text: 'Make the selected text bold.',
23521         cls: 'x-html-editor-tip'
23522     },
23523     italic : {
23524         title: 'Italic (Ctrl+I)',
23525         text: 'Make the selected text italic.',
23526         cls: 'x-html-editor-tip'
23527     },
23528     ...
23529 </code></pre>
23530     * @type Object
23531      */
23532     buttonTips : {
23533         bold : {
23534             title: 'Bold (Ctrl+B)',
23535             text: 'Make the selected text bold.',
23536             cls: 'x-html-editor-tip'
23537         },
23538         italic : {
23539             title: 'Italic (Ctrl+I)',
23540             text: 'Make the selected text italic.',
23541             cls: 'x-html-editor-tip'
23542         },
23543         underline : {
23544             title: 'Underline (Ctrl+U)',
23545             text: 'Underline the selected text.',
23546             cls: 'x-html-editor-tip'
23547         },
23548         strikethrough : {
23549             title: 'Strikethrough',
23550             text: 'Strikethrough the selected text.',
23551             cls: 'x-html-editor-tip'
23552         },
23553         increasefontsize : {
23554             title: 'Grow Text',
23555             text: 'Increase the font size.',
23556             cls: 'x-html-editor-tip'
23557         },
23558         decreasefontsize : {
23559             title: 'Shrink Text',
23560             text: 'Decrease the font size.',
23561             cls: 'x-html-editor-tip'
23562         },
23563         backcolor : {
23564             title: 'Text Highlight Color',
23565             text: 'Change the background color of the selected text.',
23566             cls: 'x-html-editor-tip'
23567         },
23568         forecolor : {
23569             title: 'Font Color',
23570             text: 'Change the color of the selected text.',
23571             cls: 'x-html-editor-tip'
23572         },
23573         justifyleft : {
23574             title: 'Align Text Left',
23575             text: 'Align text to the left.',
23576             cls: 'x-html-editor-tip'
23577         },
23578         justifycenter : {
23579             title: 'Center Text',
23580             text: 'Center text in the editor.',
23581             cls: 'x-html-editor-tip'
23582         },
23583         justifyright : {
23584             title: 'Align Text Right',
23585             text: 'Align text to the right.',
23586             cls: 'x-html-editor-tip'
23587         },
23588         insertunorderedlist : {
23589             title: 'Bullet List',
23590             text: 'Start a bulleted list.',
23591             cls: 'x-html-editor-tip'
23592         },
23593         insertorderedlist : {
23594             title: 'Numbered List',
23595             text: 'Start a numbered list.',
23596             cls: 'x-html-editor-tip'
23597         },
23598         createlink : {
23599             title: 'Hyperlink',
23600             text: 'Make the selected text a hyperlink.',
23601             cls: 'x-html-editor-tip'
23602         },
23603         sourceedit : {
23604             title: 'Source Edit',
23605             text: 'Switch to source editing mode.',
23606             cls: 'x-html-editor-tip'
23607         }
23608     },
23609     // private
23610     onDestroy : function(){
23611         if(this.rendered){
23612             
23613             this.tb.items.each(function(item){
23614                 if(item.menu){
23615                     item.menu.removeAll();
23616                     if(item.menu.el){
23617                         item.menu.el.destroy();
23618                     }
23619                 }
23620                 item.destroy();
23621             });
23622              
23623         }
23624     },
23625     onFirstFocus: function() {
23626         this.tb.items.each(function(item){
23627            item.enable();
23628         });
23629     }
23630 });
23631
23632
23633
23634
23635 // <script type="text/javascript">
23636 /*
23637  * Based on
23638  * Ext JS Library 1.1.1
23639  * Copyright(c) 2006-2007, Ext JS, LLC.
23640  *  
23641  
23642  */
23643
23644  
23645 /**
23646  * @class Roo.form.HtmlEditor.ToolbarContext
23647  * Context Toolbar
23648  * 
23649  * Usage:
23650  *
23651  new Roo.form.HtmlEditor({
23652     ....
23653     toolbars : [
23654         { xtype: 'ToolbarStandard', styles : {} }
23655         { xtype: 'ToolbarContext', disable : {} }
23656     ]
23657 })
23658
23659      
23660  * 
23661  * @config : {Object} disable List of elements to disable.. (not done yet.)
23662  * @config : {Object} styles  Map of styles available.
23663  * 
23664  */
23665
23666 Roo.form.HtmlEditor.ToolbarContext = function(config)
23667 {
23668     
23669     Roo.apply(this, config);
23670     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23671     // dont call parent... till later.
23672     this.styles = this.styles || {};
23673 }
23674
23675  
23676
23677 Roo.form.HtmlEditor.ToolbarContext.types = {
23678     'IMG' : {
23679         width : {
23680             title: "Width",
23681             width: 40
23682         },
23683         height:  {
23684             title: "Height",
23685             width: 40
23686         },
23687         align: {
23688             title: "Align",
23689             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23690             width : 80
23691             
23692         },
23693         border: {
23694             title: "Border",
23695             width: 40
23696         },
23697         alt: {
23698             title: "Alt",
23699             width: 120
23700         },
23701         src : {
23702             title: "Src",
23703             width: 220
23704         }
23705         
23706     },
23707     'A' : {
23708         name : {
23709             title: "Name",
23710             width: 50
23711         },
23712         target:  {
23713             title: "Target",
23714             width: 120
23715         },
23716         href:  {
23717             title: "Href",
23718             width: 220
23719         } // border?
23720         
23721     },
23722     'TABLE' : {
23723         rows : {
23724             title: "Rows",
23725             width: 20
23726         },
23727         cols : {
23728             title: "Cols",
23729             width: 20
23730         },
23731         width : {
23732             title: "Width",
23733             width: 40
23734         },
23735         height : {
23736             title: "Height",
23737             width: 40
23738         },
23739         border : {
23740             title: "Border",
23741             width: 20
23742         }
23743     },
23744     'TD' : {
23745         width : {
23746             title: "Width",
23747             width: 40
23748         },
23749         height : {
23750             title: "Height",
23751             width: 40
23752         },   
23753         align: {
23754             title: "Align",
23755             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23756             width: 80
23757         },
23758         valign: {
23759             title: "Valign",
23760             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23761             width: 80
23762         },
23763         colspan: {
23764             title: "Colspan",
23765             width: 20
23766             
23767         },
23768          'font-family'  : {
23769             title : "Font",
23770             style : 'fontFamily',
23771             displayField: 'display',
23772             optname : 'font-family',
23773             width: 140
23774         }
23775     },
23776     'INPUT' : {
23777         name : {
23778             title: "name",
23779             width: 120
23780         },
23781         value : {
23782             title: "Value",
23783             width: 120
23784         },
23785         width : {
23786             title: "Width",
23787             width: 40
23788         }
23789     },
23790     'LABEL' : {
23791         'for' : {
23792             title: "For",
23793             width: 120
23794         }
23795     },
23796     'TEXTAREA' : {
23797           name : {
23798             title: "name",
23799             width: 120
23800         },
23801         rows : {
23802             title: "Rows",
23803             width: 20
23804         },
23805         cols : {
23806             title: "Cols",
23807             width: 20
23808         }
23809     },
23810     'SELECT' : {
23811         name : {
23812             title: "name",
23813             width: 120
23814         },
23815         selectoptions : {
23816             title: "Options",
23817             width: 200
23818         }
23819     },
23820     
23821     // should we really allow this??
23822     // should this just be 
23823     'BODY' : {
23824         title : {
23825             title: "Title",
23826             width: 200,
23827             disabled : true
23828         }
23829     },
23830     'SPAN' : {
23831         'font-family'  : {
23832             title : "Font",
23833             style : 'fontFamily',
23834             displayField: 'display',
23835             optname : 'font-family',
23836             width: 140
23837         }
23838     },
23839     'DIV' : {
23840         'font-family'  : {
23841             title : "Font",
23842             style : 'fontFamily',
23843             displayField: 'display',
23844             optname : 'font-family',
23845             width: 140
23846         }
23847     },
23848      'P' : {
23849         'font-family'  : {
23850             title : "Font",
23851             style : 'fontFamily',
23852             displayField: 'display',
23853             optname : 'font-family',
23854             width: 140
23855         }
23856     },
23857     
23858     '*' : {
23859         // empty..
23860     }
23861
23862 };
23863
23864 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23865 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23866
23867 Roo.form.HtmlEditor.ToolbarContext.options = {
23868         'font-family'  : [ 
23869                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23870                 [ 'Courier New', 'Courier New'],
23871                 [ 'Tahoma', 'Tahoma'],
23872                 [ 'Times New Roman,serif', 'Times'],
23873                 [ 'Verdana','Verdana' ]
23874         ]
23875 };
23876
23877 // fixme - these need to be configurable..
23878  
23879
23880 //Roo.form.HtmlEditor.ToolbarContext.types
23881
23882
23883 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23884     
23885     tb: false,
23886     
23887     rendered: false,
23888     
23889     editor : false,
23890     editorcore : false,
23891     /**
23892      * @cfg {Object} disable  List of toolbar elements to disable
23893          
23894      */
23895     disable : false,
23896     /**
23897      * @cfg {Object} styles List of styles 
23898      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23899      *
23900      * These must be defined in the page, so they get rendered correctly..
23901      * .headline { }
23902      * TD.underline { }
23903      * 
23904      */
23905     styles : false,
23906     
23907     options: false,
23908     
23909     toolbars : false,
23910     
23911     init : function(editor)
23912     {
23913         this.editor = editor;
23914         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23915         var editorcore = this.editorcore;
23916         
23917         var fid = editorcore.frameId;
23918         var etb = this;
23919         function btn(id, toggle, handler){
23920             var xid = fid + '-'+ id ;
23921             return {
23922                 id : xid,
23923                 cmd : id,
23924                 cls : 'x-btn-icon x-edit-'+id,
23925                 enableToggle:toggle !== false,
23926                 scope: editorcore, // was editor...
23927                 handler:handler||editorcore.relayBtnCmd,
23928                 clickEvent:'mousedown',
23929                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23930                 tabIndex:-1
23931             };
23932         }
23933         // create a new element.
23934         var wdiv = editor.wrap.createChild({
23935                 tag: 'div'
23936             }, editor.wrap.dom.firstChild.nextSibling, true);
23937         
23938         // can we do this more than once??
23939         
23940          // stop form submits
23941       
23942  
23943         // disable everything...
23944         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23945         this.toolbars = {};
23946            
23947         for (var i in  ty) {
23948           
23949             this.toolbars[i] = this.buildToolbar(ty[i],i);
23950         }
23951         this.tb = this.toolbars.BODY;
23952         this.tb.el.show();
23953         this.buildFooter();
23954         this.footer.show();
23955         editor.on('hide', function( ) { this.footer.hide() }, this);
23956         editor.on('show', function( ) { this.footer.show() }, this);
23957         
23958          
23959         this.rendered = true;
23960         
23961         // the all the btns;
23962         editor.on('editorevent', this.updateToolbar, this);
23963         // other toolbars need to implement this..
23964         //editor.on('editmodechange', this.updateToolbar, this);
23965     },
23966     
23967     
23968     
23969     /**
23970      * Protected method that will not generally be called directly. It triggers
23971      * a toolbar update by reading the markup state of the current selection in the editor.
23972      *
23973      * Note you can force an update by calling on('editorevent', scope, false)
23974      */
23975     updateToolbar: function(editor,ev,sel){
23976
23977         //Roo.log(ev);
23978         // capture mouse up - this is handy for selecting images..
23979         // perhaps should go somewhere else...
23980         if(!this.editorcore.activated){
23981              this.editor.onFirstFocus();
23982             return;
23983         }
23984         
23985         
23986         
23987         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23988         // selectNode - might want to handle IE?
23989         if (ev &&
23990             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23991             ev.target && ev.target.tagName == 'IMG') {
23992             // they have click on an image...
23993             // let's see if we can change the selection...
23994             sel = ev.target;
23995          
23996               var nodeRange = sel.ownerDocument.createRange();
23997             try {
23998                 nodeRange.selectNode(sel);
23999             } catch (e) {
24000                 nodeRange.selectNodeContents(sel);
24001             }
24002             //nodeRange.collapse(true);
24003             var s = this.editorcore.win.getSelection();
24004             s.removeAllRanges();
24005             s.addRange(nodeRange);
24006         }  
24007         
24008       
24009         var updateFooter = sel ? false : true;
24010         
24011         
24012         var ans = this.editorcore.getAllAncestors();
24013         
24014         // pick
24015         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24016         
24017         if (!sel) { 
24018             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24019             sel = sel ? sel : this.editorcore.doc.body;
24020             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24021             
24022         }
24023         // pick a menu that exists..
24024         var tn = sel.tagName.toUpperCase();
24025         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24026         
24027         tn = sel.tagName.toUpperCase();
24028         
24029         var lastSel = this.tb.selectedNode;
24030         
24031         this.tb.selectedNode = sel;
24032         
24033         // if current menu does not match..
24034         
24035         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24036                 
24037             this.tb.el.hide();
24038             ///console.log("show: " + tn);
24039             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24040             this.tb.el.show();
24041             // update name
24042             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24043             
24044             
24045             // update attributes
24046             if (this.tb.fields) {
24047                 this.tb.fields.each(function(e) {
24048                     if (e.stylename) {
24049                         e.setValue(sel.style[e.stylename]);
24050                         return;
24051                     } 
24052                    e.setValue(sel.getAttribute(e.attrname));
24053                 });
24054             }
24055             
24056             var hasStyles = false;
24057             for(var i in this.styles) {
24058                 hasStyles = true;
24059                 break;
24060             }
24061             
24062             // update styles
24063             if (hasStyles) { 
24064                 var st = this.tb.fields.item(0);
24065                 
24066                 st.store.removeAll();
24067                
24068                 
24069                 var cn = sel.className.split(/\s+/);
24070                 
24071                 var avs = [];
24072                 if (this.styles['*']) {
24073                     
24074                     Roo.each(this.styles['*'], function(v) {
24075                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24076                     });
24077                 }
24078                 if (this.styles[tn]) { 
24079                     Roo.each(this.styles[tn], function(v) {
24080                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24081                     });
24082                 }
24083                 
24084                 st.store.loadData(avs);
24085                 st.collapse();
24086                 st.setValue(cn);
24087             }
24088             // flag our selected Node.
24089             this.tb.selectedNode = sel;
24090            
24091            
24092             Roo.menu.MenuMgr.hideAll();
24093
24094         }
24095         
24096         if (!updateFooter) {
24097             //this.footDisp.dom.innerHTML = ''; 
24098             return;
24099         }
24100         // update the footer
24101         //
24102         var html = '';
24103         
24104         this.footerEls = ans.reverse();
24105         Roo.each(this.footerEls, function(a,i) {
24106             if (!a) { return; }
24107             html += html.length ? ' &gt; '  :  '';
24108             
24109             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24110             
24111         });
24112        
24113         // 
24114         var sz = this.footDisp.up('td').getSize();
24115         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24116         this.footDisp.dom.style.marginLeft = '5px';
24117         
24118         this.footDisp.dom.style.overflow = 'hidden';
24119         
24120         this.footDisp.dom.innerHTML = html;
24121             
24122         //this.editorsyncValue();
24123     },
24124      
24125     
24126    
24127        
24128     // private
24129     onDestroy : function(){
24130         if(this.rendered){
24131             
24132             this.tb.items.each(function(item){
24133                 if(item.menu){
24134                     item.menu.removeAll();
24135                     if(item.menu.el){
24136                         item.menu.el.destroy();
24137                     }
24138                 }
24139                 item.destroy();
24140             });
24141              
24142         }
24143     },
24144     onFirstFocus: function() {
24145         // need to do this for all the toolbars..
24146         this.tb.items.each(function(item){
24147            item.enable();
24148         });
24149     },
24150     buildToolbar: function(tlist, nm)
24151     {
24152         var editor = this.editor;
24153         var editorcore = this.editorcore;
24154          // create a new element.
24155         var wdiv = editor.wrap.createChild({
24156                 tag: 'div'
24157             }, editor.wrap.dom.firstChild.nextSibling, true);
24158         
24159        
24160         var tb = new Roo.Toolbar(wdiv);
24161         // add the name..
24162         
24163         tb.add(nm+ ":&nbsp;");
24164         
24165         var styles = [];
24166         for(var i in this.styles) {
24167             styles.push(i);
24168         }
24169         
24170         // styles...
24171         if (styles && styles.length) {
24172             
24173             // this needs a multi-select checkbox...
24174             tb.addField( new Roo.form.ComboBox({
24175                 store: new Roo.data.SimpleStore({
24176                     id : 'val',
24177                     fields: ['val', 'selected'],
24178                     data : [] 
24179                 }),
24180                 name : '-roo-edit-className',
24181                 attrname : 'className',
24182                 displayField: 'val',
24183                 typeAhead: false,
24184                 mode: 'local',
24185                 editable : false,
24186                 triggerAction: 'all',
24187                 emptyText:'Select Style',
24188                 selectOnFocus:true,
24189                 width: 130,
24190                 listeners : {
24191                     'select': function(c, r, i) {
24192                         // initial support only for on class per el..
24193                         tb.selectedNode.className =  r ? r.get('val') : '';
24194                         editorcore.syncValue();
24195                     }
24196                 }
24197     
24198             }));
24199         }
24200         
24201         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24202         var tbops = tbc.options;
24203         
24204         for (var i in tlist) {
24205             
24206             var item = tlist[i];
24207             tb.add(item.title + ":&nbsp;");
24208             
24209             
24210             //optname == used so you can configure the options available..
24211             var opts = item.opts ? item.opts : false;
24212             if (item.optname) {
24213                 opts = tbops[item.optname];
24214            
24215             }
24216             
24217             if (opts) {
24218                 // opts == pulldown..
24219                 tb.addField( new Roo.form.ComboBox({
24220                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24221                         id : 'val',
24222                         fields: ['val', 'display'],
24223                         data : opts  
24224                     }),
24225                     name : '-roo-edit-' + i,
24226                     attrname : i,
24227                     stylename : item.style ? item.style : false,
24228                     displayField: item.displayField ? item.displayField : 'val',
24229                     valueField :  'val',
24230                     typeAhead: false,
24231                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24232                     editable : false,
24233                     triggerAction: 'all',
24234                     emptyText:'Select',
24235                     selectOnFocus:true,
24236                     width: item.width ? item.width  : 130,
24237                     listeners : {
24238                         'select': function(c, r, i) {
24239                             if (c.stylename) {
24240                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24241                                 return;
24242                             }
24243                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24244                         }
24245                     }
24246
24247                 }));
24248                 continue;
24249                     
24250                  
24251                 
24252                 tb.addField( new Roo.form.TextField({
24253                     name: i,
24254                     width: 100,
24255                     //allowBlank:false,
24256                     value: ''
24257                 }));
24258                 continue;
24259             }
24260             tb.addField( new Roo.form.TextField({
24261                 name: '-roo-edit-' + i,
24262                 attrname : i,
24263                 
24264                 width: item.width,
24265                 //allowBlank:true,
24266                 value: '',
24267                 listeners: {
24268                     'change' : function(f, nv, ov) {
24269                         tb.selectedNode.setAttribute(f.attrname, nv);
24270                         editorcore.syncValue();
24271                     }
24272                 }
24273             }));
24274              
24275         }
24276         
24277         var _this = this;
24278         
24279         if(nm == 'BODY'){
24280             tb.addSeparator();
24281         
24282             tb.addButton( {
24283                 text: 'Stylesheets',
24284
24285                 listeners : {
24286                     click : function ()
24287                     {
24288                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24289                     }
24290                 }
24291             });
24292         }
24293         
24294         tb.addFill();
24295         tb.addButton( {
24296             text: 'Remove Tag',
24297     
24298             listeners : {
24299                 click : function ()
24300                 {
24301                     // remove
24302                     // undo does not work.
24303                      
24304                     var sn = tb.selectedNode;
24305                     
24306                     var pn = sn.parentNode;
24307                     
24308                     var stn =  sn.childNodes[0];
24309                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24310                     while (sn.childNodes.length) {
24311                         var node = sn.childNodes[0];
24312                         sn.removeChild(node);
24313                         //Roo.log(node);
24314                         pn.insertBefore(node, sn);
24315                         
24316                     }
24317                     pn.removeChild(sn);
24318                     var range = editorcore.createRange();
24319         
24320                     range.setStart(stn,0);
24321                     range.setEnd(en,0); //????
24322                     //range.selectNode(sel);
24323                     
24324                     
24325                     var selection = editorcore.getSelection();
24326                     selection.removeAllRanges();
24327                     selection.addRange(range);
24328                     
24329                     
24330                     
24331                     //_this.updateToolbar(null, null, pn);
24332                     _this.updateToolbar(null, null, null);
24333                     _this.footDisp.dom.innerHTML = ''; 
24334                 }
24335             }
24336             
24337                     
24338                 
24339             
24340         });
24341         
24342         
24343         tb.el.on('click', function(e){
24344             e.preventDefault(); // what does this do?
24345         });
24346         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24347         tb.el.hide();
24348         tb.name = nm;
24349         // dont need to disable them... as they will get hidden
24350         return tb;
24351          
24352         
24353     },
24354     buildFooter : function()
24355     {
24356         
24357         var fel = this.editor.wrap.createChild();
24358         this.footer = new Roo.Toolbar(fel);
24359         // toolbar has scrolly on left / right?
24360         var footDisp= new Roo.Toolbar.Fill();
24361         var _t = this;
24362         this.footer.add(
24363             {
24364                 text : '&lt;',
24365                 xtype: 'Button',
24366                 handler : function() {
24367                     _t.footDisp.scrollTo('left',0,true)
24368                 }
24369             }
24370         );
24371         this.footer.add( footDisp );
24372         this.footer.add( 
24373             {
24374                 text : '&gt;',
24375                 xtype: 'Button',
24376                 handler : function() {
24377                     // no animation..
24378                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24379                 }
24380             }
24381         );
24382         var fel = Roo.get(footDisp.el);
24383         fel.addClass('x-editor-context');
24384         this.footDispWrap = fel; 
24385         this.footDispWrap.overflow  = 'hidden';
24386         
24387         this.footDisp = fel.createChild();
24388         this.footDispWrap.on('click', this.onContextClick, this)
24389         
24390         
24391     },
24392     onContextClick : function (ev,dom)
24393     {
24394         ev.preventDefault();
24395         var  cn = dom.className;
24396         //Roo.log(cn);
24397         if (!cn.match(/x-ed-loc-/)) {
24398             return;
24399         }
24400         var n = cn.split('-').pop();
24401         var ans = this.footerEls;
24402         var sel = ans[n];
24403         
24404          // pick
24405         var range = this.editorcore.createRange();
24406         
24407         range.selectNodeContents(sel);
24408         //range.selectNode(sel);
24409         
24410         
24411         var selection = this.editorcore.getSelection();
24412         selection.removeAllRanges();
24413         selection.addRange(range);
24414         
24415         
24416         
24417         this.updateToolbar(null, null, sel);
24418         
24419         
24420     }
24421     
24422     
24423     
24424     
24425     
24426 });
24427
24428
24429
24430
24431
24432 /*
24433  * Based on:
24434  * Ext JS Library 1.1.1
24435  * Copyright(c) 2006-2007, Ext JS, LLC.
24436  *
24437  * Originally Released Under LGPL - original licence link has changed is not relivant.
24438  *
24439  * Fork - LGPL
24440  * <script type="text/javascript">
24441  */
24442  
24443 /**
24444  * @class Roo.form.BasicForm
24445  * @extends Roo.util.Observable
24446  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24447  * @constructor
24448  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24449  * @param {Object} config Configuration options
24450  */
24451 Roo.form.BasicForm = function(el, config){
24452     this.allItems = [];
24453     this.childForms = [];
24454     Roo.apply(this, config);
24455     /*
24456      * The Roo.form.Field items in this form.
24457      * @type MixedCollection
24458      */
24459      
24460      
24461     this.items = new Roo.util.MixedCollection(false, function(o){
24462         return o.id || (o.id = Roo.id());
24463     });
24464     this.addEvents({
24465         /**
24466          * @event beforeaction
24467          * Fires before any action is performed. Return false to cancel the action.
24468          * @param {Form} this
24469          * @param {Action} action The action to be performed
24470          */
24471         beforeaction: true,
24472         /**
24473          * @event actionfailed
24474          * Fires when an action fails.
24475          * @param {Form} this
24476          * @param {Action} action The action that failed
24477          */
24478         actionfailed : true,
24479         /**
24480          * @event actioncomplete
24481          * Fires when an action is completed.
24482          * @param {Form} this
24483          * @param {Action} action The action that completed
24484          */
24485         actioncomplete : true
24486     });
24487     if(el){
24488         this.initEl(el);
24489     }
24490     Roo.form.BasicForm.superclass.constructor.call(this);
24491     
24492     Roo.form.BasicForm.popover.apply();
24493 };
24494
24495 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24496     /**
24497      * @cfg {String} method
24498      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24499      */
24500     /**
24501      * @cfg {DataReader} reader
24502      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24503      * This is optional as there is built-in support for processing JSON.
24504      */
24505     /**
24506      * @cfg {DataReader} errorReader
24507      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24508      * This is completely optional as there is built-in support for processing JSON.
24509      */
24510     /**
24511      * @cfg {String} url
24512      * The URL to use for form actions if one isn't supplied in the action options.
24513      */
24514     /**
24515      * @cfg {Boolean} fileUpload
24516      * Set to true if this form is a file upload.
24517      */
24518      
24519     /**
24520      * @cfg {Object} baseParams
24521      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24522      */
24523      /**
24524      
24525     /**
24526      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24527      */
24528     timeout: 30,
24529
24530     // private
24531     activeAction : null,
24532
24533     /**
24534      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24535      * or setValues() data instead of when the form was first created.
24536      */
24537     trackResetOnLoad : false,
24538     
24539     
24540     /**
24541      * childForms - used for multi-tab forms
24542      * @type {Array}
24543      */
24544     childForms : false,
24545     
24546     /**
24547      * allItems - full list of fields.
24548      * @type {Array}
24549      */
24550     allItems : false,
24551     
24552     /**
24553      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24554      * element by passing it or its id or mask the form itself by passing in true.
24555      * @type Mixed
24556      */
24557     waitMsgTarget : false,
24558     
24559     /**
24560      * @type Boolean
24561      */
24562     disableMask : false,
24563     
24564     /**
24565      * @cfg {Boolean} errorMask (true|false) default false
24566      */
24567     errorMask : false,
24568     
24569     /**
24570      * @cfg {Number} maskOffset Default 100
24571      */
24572     maskOffset : 100,
24573
24574     // private
24575     initEl : function(el){
24576         this.el = Roo.get(el);
24577         this.id = this.el.id || Roo.id();
24578         this.el.on('submit', this.onSubmit, this);
24579         this.el.addClass('x-form');
24580     },
24581
24582     // private
24583     onSubmit : function(e){
24584         e.stopEvent();
24585     },
24586
24587     /**
24588      * Returns true if client-side validation on the form is successful.
24589      * @return Boolean
24590      */
24591     isValid : function(){
24592         var valid = true;
24593         var target = false;
24594         this.items.each(function(f){
24595             if(f.validate()){
24596                 return;
24597             }
24598             
24599             valid = false;
24600                 
24601             if(!target && f.el.isVisible(true)){
24602                 target = f;
24603             }
24604         });
24605         
24606         if(this.errorMask && !valid){
24607             Roo.form.BasicForm.popover.mask(this, target);
24608         }
24609         
24610         return valid;
24611     },
24612     /**
24613      * Returns array of invalid form fields.
24614      * @return Array
24615      */
24616     
24617     invalidFields : function()
24618     {
24619         var ret = [];
24620         this.items.each(function(f){
24621             if(f.validate()){
24622                 return;
24623             }
24624             ret.push(f);
24625             
24626         });
24627         
24628         return ret;
24629     },
24630     
24631     
24632     /**
24633      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24634      * @return Boolean
24635      */
24636     isDirty : function(){
24637         var dirty = false;
24638         this.items.each(function(f){
24639            if(f.isDirty()){
24640                dirty = true;
24641                return false;
24642            }
24643         });
24644         return dirty;
24645     },
24646     
24647     /**
24648      * Returns true if any fields in this form have changed since their original load. (New version)
24649      * @return Boolean
24650      */
24651     
24652     hasChanged : function()
24653     {
24654         var dirty = false;
24655         this.items.each(function(f){
24656            if(f.hasChanged()){
24657                dirty = true;
24658                return false;
24659            }
24660         });
24661         return dirty;
24662         
24663     },
24664     /**
24665      * Resets all hasChanged to 'false' -
24666      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24667      * So hasChanged storage is only to be used for this purpose
24668      * @return Boolean
24669      */
24670     resetHasChanged : function()
24671     {
24672         this.items.each(function(f){
24673            f.resetHasChanged();
24674         });
24675         
24676     },
24677     
24678     
24679     /**
24680      * Performs a predefined action (submit or load) or custom actions you define on this form.
24681      * @param {String} actionName The name of the action type
24682      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24683      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24684      * accept other config options):
24685      * <pre>
24686 Property          Type             Description
24687 ----------------  ---------------  ----------------------------------------------------------------------------------
24688 url               String           The url for the action (defaults to the form's url)
24689 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24690 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24691 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24692                                    validate the form on the client (defaults to false)
24693      * </pre>
24694      * @return {BasicForm} this
24695      */
24696     doAction : function(action, options){
24697         if(typeof action == 'string'){
24698             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24699         }
24700         if(this.fireEvent('beforeaction', this, action) !== false){
24701             this.beforeAction(action);
24702             action.run.defer(100, action);
24703         }
24704         return this;
24705     },
24706
24707     /**
24708      * Shortcut to do a submit action.
24709      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24710      * @return {BasicForm} this
24711      */
24712     submit : function(options){
24713         this.doAction('submit', options);
24714         return this;
24715     },
24716
24717     /**
24718      * Shortcut to do a load action.
24719      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24720      * @return {BasicForm} this
24721      */
24722     load : function(options){
24723         this.doAction('load', options);
24724         return this;
24725     },
24726
24727     /**
24728      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24729      * @param {Record} record The record to edit
24730      * @return {BasicForm} this
24731      */
24732     updateRecord : function(record){
24733         record.beginEdit();
24734         var fs = record.fields;
24735         fs.each(function(f){
24736             var field = this.findField(f.name);
24737             if(field){
24738                 record.set(f.name, field.getValue());
24739             }
24740         }, this);
24741         record.endEdit();
24742         return this;
24743     },
24744
24745     /**
24746      * Loads an Roo.data.Record into this form.
24747      * @param {Record} record The record to load
24748      * @return {BasicForm} this
24749      */
24750     loadRecord : function(record){
24751         this.setValues(record.data);
24752         return this;
24753     },
24754
24755     // private
24756     beforeAction : function(action){
24757         var o = action.options;
24758         
24759         if(!this.disableMask) {
24760             if(this.waitMsgTarget === true){
24761                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24762             }else if(this.waitMsgTarget){
24763                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24764                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24765             }else {
24766                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24767             }
24768         }
24769         
24770          
24771     },
24772
24773     // private
24774     afterAction : function(action, success){
24775         this.activeAction = null;
24776         var o = action.options;
24777         
24778         if(!this.disableMask) {
24779             if(this.waitMsgTarget === true){
24780                 this.el.unmask();
24781             }else if(this.waitMsgTarget){
24782                 this.waitMsgTarget.unmask();
24783             }else{
24784                 Roo.MessageBox.updateProgress(1);
24785                 Roo.MessageBox.hide();
24786             }
24787         }
24788         
24789         if(success){
24790             if(o.reset){
24791                 this.reset();
24792             }
24793             Roo.callback(o.success, o.scope, [this, action]);
24794             this.fireEvent('actioncomplete', this, action);
24795             
24796         }else{
24797             
24798             // failure condition..
24799             // we have a scenario where updates need confirming.
24800             // eg. if a locking scenario exists..
24801             // we look for { errors : { needs_confirm : true }} in the response.
24802             if (
24803                 (typeof(action.result) != 'undefined')  &&
24804                 (typeof(action.result.errors) != 'undefined')  &&
24805                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24806            ){
24807                 var _t = this;
24808                 Roo.MessageBox.confirm(
24809                     "Change requires confirmation",
24810                     action.result.errorMsg,
24811                     function(r) {
24812                         if (r != 'yes') {
24813                             return;
24814                         }
24815                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24816                     }
24817                     
24818                 );
24819                 
24820                 
24821                 
24822                 return;
24823             }
24824             
24825             Roo.callback(o.failure, o.scope, [this, action]);
24826             // show an error message if no failed handler is set..
24827             if (!this.hasListener('actionfailed')) {
24828                 Roo.MessageBox.alert("Error",
24829                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24830                         action.result.errorMsg :
24831                         "Saving Failed, please check your entries or try again"
24832                 );
24833             }
24834             
24835             this.fireEvent('actionfailed', this, action);
24836         }
24837         
24838     },
24839
24840     /**
24841      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24842      * @param {String} id The value to search for
24843      * @return Field
24844      */
24845     findField : function(id){
24846         var field = this.items.get(id);
24847         if(!field){
24848             this.items.each(function(f){
24849                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24850                     field = f;
24851                     return false;
24852                 }
24853             });
24854         }
24855         return field || null;
24856     },
24857
24858     /**
24859      * Add a secondary form to this one, 
24860      * Used to provide tabbed forms. One form is primary, with hidden values 
24861      * which mirror the elements from the other forms.
24862      * 
24863      * @param {Roo.form.Form} form to add.
24864      * 
24865      */
24866     addForm : function(form)
24867     {
24868        
24869         if (this.childForms.indexOf(form) > -1) {
24870             // already added..
24871             return;
24872         }
24873         this.childForms.push(form);
24874         var n = '';
24875         Roo.each(form.allItems, function (fe) {
24876             
24877             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24878             if (this.findField(n)) { // already added..
24879                 return;
24880             }
24881             var add = new Roo.form.Hidden({
24882                 name : n
24883             });
24884             add.render(this.el);
24885             
24886             this.add( add );
24887         }, this);
24888         
24889     },
24890     /**
24891      * Mark fields in this form invalid in bulk.
24892      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24893      * @return {BasicForm} this
24894      */
24895     markInvalid : function(errors){
24896         if(errors instanceof Array){
24897             for(var i = 0, len = errors.length; i < len; i++){
24898                 var fieldError = errors[i];
24899                 var f = this.findField(fieldError.id);
24900                 if(f){
24901                     f.markInvalid(fieldError.msg);
24902                 }
24903             }
24904         }else{
24905             var field, id;
24906             for(id in errors){
24907                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24908                     field.markInvalid(errors[id]);
24909                 }
24910             }
24911         }
24912         Roo.each(this.childForms || [], function (f) {
24913             f.markInvalid(errors);
24914         });
24915         
24916         return this;
24917     },
24918
24919     /**
24920      * Set values for fields in this form in bulk.
24921      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24922      * @return {BasicForm} this
24923      */
24924     setValues : function(values){
24925         if(values instanceof Array){ // array of objects
24926             for(var i = 0, len = values.length; i < len; i++){
24927                 var v = values[i];
24928                 var f = this.findField(v.id);
24929                 if(f){
24930                     f.setValue(v.value);
24931                     if(this.trackResetOnLoad){
24932                         f.originalValue = f.getValue();
24933                     }
24934                 }
24935             }
24936         }else{ // object hash
24937             var field, id;
24938             for(id in values){
24939                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24940                     
24941                     if (field.setFromData && 
24942                         field.valueField && 
24943                         field.displayField &&
24944                         // combos' with local stores can 
24945                         // be queried via setValue()
24946                         // to set their value..
24947                         (field.store && !field.store.isLocal)
24948                         ) {
24949                         // it's a combo
24950                         var sd = { };
24951                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24952                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24953                         field.setFromData(sd);
24954                         
24955                     } else {
24956                         field.setValue(values[id]);
24957                     }
24958                     
24959                     
24960                     if(this.trackResetOnLoad){
24961                         field.originalValue = field.getValue();
24962                     }
24963                 }
24964             }
24965         }
24966         this.resetHasChanged();
24967         
24968         
24969         Roo.each(this.childForms || [], function (f) {
24970             f.setValues(values);
24971             f.resetHasChanged();
24972         });
24973                 
24974         return this;
24975     },
24976  
24977     /**
24978      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24979      * they are returned as an array.
24980      * @param {Boolean} asString
24981      * @return {Object}
24982      */
24983     getValues : function(asString){
24984         if (this.childForms) {
24985             // copy values from the child forms
24986             Roo.each(this.childForms, function (f) {
24987                 this.setValues(f.getValues());
24988             }, this);
24989         }
24990         
24991         // use formdata
24992         if (typeof(FormData) != 'undefined' && asString !== true) {
24993             // this relies on a 'recent' version of chrome apparently...
24994             try {
24995                 var fd = (new FormData(this.el.dom)).entries();
24996                 var ret = {};
24997                 var ent = fd.next();
24998                 while (!ent.done) {
24999                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25000                     ent = fd.next();
25001                 };
25002                 return ret;
25003             } catch(e) {
25004                 
25005             }
25006             
25007         }
25008         
25009         
25010         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25011         if(asString === true){
25012             return fs;
25013         }
25014         return Roo.urlDecode(fs);
25015     },
25016     
25017     /**
25018      * Returns the fields in this form as an object with key/value pairs. 
25019      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25020      * @return {Object}
25021      */
25022     getFieldValues : function(with_hidden)
25023     {
25024         if (this.childForms) {
25025             // copy values from the child forms
25026             // should this call getFieldValues - probably not as we do not currently copy
25027             // hidden fields when we generate..
25028             Roo.each(this.childForms, function (f) {
25029                 this.setValues(f.getValues());
25030             }, this);
25031         }
25032         
25033         var ret = {};
25034         this.items.each(function(f){
25035             if (!f.getName()) {
25036                 return;
25037             }
25038             var v = f.getValue();
25039             if (f.inputType =='radio') {
25040                 if (typeof(ret[f.getName()]) == 'undefined') {
25041                     ret[f.getName()] = ''; // empty..
25042                 }
25043                 
25044                 if (!f.el.dom.checked) {
25045                     return;
25046                     
25047                 }
25048                 v = f.el.dom.value;
25049                 
25050             }
25051             
25052             // not sure if this supported any more..
25053             if ((typeof(v) == 'object') && f.getRawValue) {
25054                 v = f.getRawValue() ; // dates..
25055             }
25056             // combo boxes where name != hiddenName...
25057             if (f.name != f.getName()) {
25058                 ret[f.name] = f.getRawValue();
25059             }
25060             ret[f.getName()] = v;
25061         });
25062         
25063         return ret;
25064     },
25065
25066     /**
25067      * Clears all invalid messages in this form.
25068      * @return {BasicForm} this
25069      */
25070     clearInvalid : function(){
25071         this.items.each(function(f){
25072            f.clearInvalid();
25073         });
25074         
25075         Roo.each(this.childForms || [], function (f) {
25076             f.clearInvalid();
25077         });
25078         
25079         
25080         return this;
25081     },
25082
25083     /**
25084      * Resets this form.
25085      * @return {BasicForm} this
25086      */
25087     reset : function(){
25088         this.items.each(function(f){
25089             f.reset();
25090         });
25091         
25092         Roo.each(this.childForms || [], function (f) {
25093             f.reset();
25094         });
25095         this.resetHasChanged();
25096         
25097         return this;
25098     },
25099
25100     /**
25101      * Add Roo.form components to this form.
25102      * @param {Field} field1
25103      * @param {Field} field2 (optional)
25104      * @param {Field} etc (optional)
25105      * @return {BasicForm} this
25106      */
25107     add : function(){
25108         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25109         return this;
25110     },
25111
25112
25113     /**
25114      * Removes a field from the items collection (does NOT remove its markup).
25115      * @param {Field} field
25116      * @return {BasicForm} this
25117      */
25118     remove : function(field){
25119         this.items.remove(field);
25120         return this;
25121     },
25122
25123     /**
25124      * Looks at the fields in this form, checks them for an id attribute,
25125      * and calls applyTo on the existing dom element with that id.
25126      * @return {BasicForm} this
25127      */
25128     render : function(){
25129         this.items.each(function(f){
25130             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25131                 f.applyTo(f.id);
25132             }
25133         });
25134         return this;
25135     },
25136
25137     /**
25138      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25139      * @param {Object} values
25140      * @return {BasicForm} this
25141      */
25142     applyToFields : function(o){
25143         this.items.each(function(f){
25144            Roo.apply(f, o);
25145         });
25146         return this;
25147     },
25148
25149     /**
25150      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25151      * @param {Object} values
25152      * @return {BasicForm} this
25153      */
25154     applyIfToFields : function(o){
25155         this.items.each(function(f){
25156            Roo.applyIf(f, o);
25157         });
25158         return this;
25159     }
25160 });
25161
25162 // back compat
25163 Roo.BasicForm = Roo.form.BasicForm;
25164
25165 Roo.apply(Roo.form.BasicForm, {
25166     
25167     popover : {
25168         
25169         padding : 5,
25170         
25171         isApplied : false,
25172         
25173         isMasked : false,
25174         
25175         form : false,
25176         
25177         target : false,
25178         
25179         intervalID : false,
25180         
25181         maskEl : false,
25182         
25183         apply : function()
25184         {
25185             if(this.isApplied){
25186                 return;
25187             }
25188             
25189             this.maskEl = {
25190                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25191                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25192                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25193                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25194             };
25195             
25196             this.maskEl.top.enableDisplayMode("block");
25197             this.maskEl.left.enableDisplayMode("block");
25198             this.maskEl.bottom.enableDisplayMode("block");
25199             this.maskEl.right.enableDisplayMode("block");
25200             
25201             Roo.get(document.body).on('click', function(){
25202                 this.unmask();
25203             }, this);
25204             
25205             Roo.get(document.body).on('touchstart', function(){
25206                 this.unmask();
25207             }, this);
25208             
25209             this.isApplied = true
25210         },
25211         
25212         mask : function(form, target)
25213         {
25214             this.form = form;
25215             
25216             this.target = target;
25217             
25218             if(!this.form.errorMask || !target.el){
25219                 return;
25220             }
25221             
25222             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25223             
25224             var ot = this.target.el.calcOffsetsTo(scrollable);
25225             
25226             var scrollTo = ot[1] - this.form.maskOffset;
25227             
25228             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25229             
25230             scrollable.scrollTo('top', scrollTo);
25231             
25232             var el = this.target.wrap || this.target.el;
25233             
25234             var box = el.getBox();
25235             
25236             this.maskEl.top.setStyle('position', 'absolute');
25237             this.maskEl.top.setStyle('z-index', 10000);
25238             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25239             this.maskEl.top.setLeft(0);
25240             this.maskEl.top.setTop(0);
25241             this.maskEl.top.show();
25242             
25243             this.maskEl.left.setStyle('position', 'absolute');
25244             this.maskEl.left.setStyle('z-index', 10000);
25245             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25246             this.maskEl.left.setLeft(0);
25247             this.maskEl.left.setTop(box.y - this.padding);
25248             this.maskEl.left.show();
25249
25250             this.maskEl.bottom.setStyle('position', 'absolute');
25251             this.maskEl.bottom.setStyle('z-index', 10000);
25252             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25253             this.maskEl.bottom.setLeft(0);
25254             this.maskEl.bottom.setTop(box.bottom + this.padding);
25255             this.maskEl.bottom.show();
25256
25257             this.maskEl.right.setStyle('position', 'absolute');
25258             this.maskEl.right.setStyle('z-index', 10000);
25259             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25260             this.maskEl.right.setLeft(box.right + this.padding);
25261             this.maskEl.right.setTop(box.y - this.padding);
25262             this.maskEl.right.show();
25263
25264             this.intervalID = window.setInterval(function() {
25265                 Roo.form.BasicForm.popover.unmask();
25266             }, 10000);
25267
25268             window.onwheel = function(){ return false;};
25269             
25270             (function(){ this.isMasked = true; }).defer(500, this);
25271             
25272         },
25273         
25274         unmask : function()
25275         {
25276             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25277                 return;
25278             }
25279             
25280             this.maskEl.top.setStyle('position', 'absolute');
25281             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25282             this.maskEl.top.hide();
25283
25284             this.maskEl.left.setStyle('position', 'absolute');
25285             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25286             this.maskEl.left.hide();
25287
25288             this.maskEl.bottom.setStyle('position', 'absolute');
25289             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25290             this.maskEl.bottom.hide();
25291
25292             this.maskEl.right.setStyle('position', 'absolute');
25293             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25294             this.maskEl.right.hide();
25295             
25296             window.onwheel = function(){ return true;};
25297             
25298             if(this.intervalID){
25299                 window.clearInterval(this.intervalID);
25300                 this.intervalID = false;
25301             }
25302             
25303             this.isMasked = false;
25304             
25305         }
25306         
25307     }
25308     
25309 });/*
25310  * Based on:
25311  * Ext JS Library 1.1.1
25312  * Copyright(c) 2006-2007, Ext JS, LLC.
25313  *
25314  * Originally Released Under LGPL - original licence link has changed is not relivant.
25315  *
25316  * Fork - LGPL
25317  * <script type="text/javascript">
25318  */
25319
25320 /**
25321  * @class Roo.form.Form
25322  * @extends Roo.form.BasicForm
25323  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25324  * @constructor
25325  * @param {Object} config Configuration options
25326  */
25327 Roo.form.Form = function(config){
25328     var xitems =  [];
25329     if (config.items) {
25330         xitems = config.items;
25331         delete config.items;
25332     }
25333    
25334     
25335     Roo.form.Form.superclass.constructor.call(this, null, config);
25336     this.url = this.url || this.action;
25337     if(!this.root){
25338         this.root = new Roo.form.Layout(Roo.applyIf({
25339             id: Roo.id()
25340         }, config));
25341     }
25342     this.active = this.root;
25343     /**
25344      * Array of all the buttons that have been added to this form via {@link addButton}
25345      * @type Array
25346      */
25347     this.buttons = [];
25348     this.allItems = [];
25349     this.addEvents({
25350         /**
25351          * @event clientvalidation
25352          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25353          * @param {Form} this
25354          * @param {Boolean} valid true if the form has passed client-side validation
25355          */
25356         clientvalidation: true,
25357         /**
25358          * @event rendered
25359          * Fires when the form is rendered
25360          * @param {Roo.form.Form} form
25361          */
25362         rendered : true
25363     });
25364     
25365     if (this.progressUrl) {
25366             // push a hidden field onto the list of fields..
25367             this.addxtype( {
25368                     xns: Roo.form, 
25369                     xtype : 'Hidden', 
25370                     name : 'UPLOAD_IDENTIFIER' 
25371             });
25372         }
25373         
25374     
25375     Roo.each(xitems, this.addxtype, this);
25376     
25377 };
25378
25379 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25380     /**
25381      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25382      */
25383     /**
25384      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25385      */
25386     /**
25387      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25388      */
25389     buttonAlign:'center',
25390
25391     /**
25392      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25393      */
25394     minButtonWidth:75,
25395
25396     /**
25397      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25398      * This property cascades to child containers if not set.
25399      */
25400     labelAlign:'left',
25401
25402     /**
25403      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25404      * fires a looping event with that state. This is required to bind buttons to the valid
25405      * state using the config value formBind:true on the button.
25406      */
25407     monitorValid : false,
25408
25409     /**
25410      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25411      */
25412     monitorPoll : 200,
25413     
25414     /**
25415      * @cfg {String} progressUrl - Url to return progress data 
25416      */
25417     
25418     progressUrl : false,
25419     /**
25420      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25421      * sending a formdata with extra parameters - eg uploaded elements.
25422      */
25423     
25424     formData : false,
25425     
25426     /**
25427      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25428      * fields are added and the column is closed. If no fields are passed the column remains open
25429      * until end() is called.
25430      * @param {Object} config The config to pass to the column
25431      * @param {Field} field1 (optional)
25432      * @param {Field} field2 (optional)
25433      * @param {Field} etc (optional)
25434      * @return Column The column container object
25435      */
25436     column : function(c){
25437         var col = new Roo.form.Column(c);
25438         this.start(col);
25439         if(arguments.length > 1){ // duplicate code required because of Opera
25440             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25441             this.end();
25442         }
25443         return col;
25444     },
25445
25446     /**
25447      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25448      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25449      * until end() is called.
25450      * @param {Object} config The config to pass to the fieldset
25451      * @param {Field} field1 (optional)
25452      * @param {Field} field2 (optional)
25453      * @param {Field} etc (optional)
25454      * @return FieldSet The fieldset container object
25455      */
25456     fieldset : function(c){
25457         var fs = new Roo.form.FieldSet(c);
25458         this.start(fs);
25459         if(arguments.length > 1){ // duplicate code required because of Opera
25460             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25461             this.end();
25462         }
25463         return fs;
25464     },
25465
25466     /**
25467      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25468      * fields are added and the container is closed. If no fields are passed the container remains open
25469      * until end() is called.
25470      * @param {Object} config The config to pass to the Layout
25471      * @param {Field} field1 (optional)
25472      * @param {Field} field2 (optional)
25473      * @param {Field} etc (optional)
25474      * @return Layout The container object
25475      */
25476     container : function(c){
25477         var l = new Roo.form.Layout(c);
25478         this.start(l);
25479         if(arguments.length > 1){ // duplicate code required because of Opera
25480             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25481             this.end();
25482         }
25483         return l;
25484     },
25485
25486     /**
25487      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25488      * @param {Object} container A Roo.form.Layout or subclass of Layout
25489      * @return {Form} this
25490      */
25491     start : function(c){
25492         // cascade label info
25493         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25494         this.active.stack.push(c);
25495         c.ownerCt = this.active;
25496         this.active = c;
25497         return this;
25498     },
25499
25500     /**
25501      * Closes the current open container
25502      * @return {Form} this
25503      */
25504     end : function(){
25505         if(this.active == this.root){
25506             return this;
25507         }
25508         this.active = this.active.ownerCt;
25509         return this;
25510     },
25511
25512     /**
25513      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25514      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25515      * as the label of the field.
25516      * @param {Field} field1
25517      * @param {Field} field2 (optional)
25518      * @param {Field} etc. (optional)
25519      * @return {Form} this
25520      */
25521     add : function(){
25522         this.active.stack.push.apply(this.active.stack, arguments);
25523         this.allItems.push.apply(this.allItems,arguments);
25524         var r = [];
25525         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25526             if(a[i].isFormField){
25527                 r.push(a[i]);
25528             }
25529         }
25530         if(r.length > 0){
25531             Roo.form.Form.superclass.add.apply(this, r);
25532         }
25533         return this;
25534     },
25535     
25536
25537     
25538     
25539     
25540      /**
25541      * Find any element that has been added to a form, using it's ID or name
25542      * This can include framesets, columns etc. along with regular fields..
25543      * @param {String} id - id or name to find.
25544      
25545      * @return {Element} e - or false if nothing found.
25546      */
25547     findbyId : function(id)
25548     {
25549         var ret = false;
25550         if (!id) {
25551             return ret;
25552         }
25553         Roo.each(this.allItems, function(f){
25554             if (f.id == id || f.name == id ){
25555                 ret = f;
25556                 return false;
25557             }
25558         });
25559         return ret;
25560     },
25561
25562     
25563     
25564     /**
25565      * Render this form into the passed container. This should only be called once!
25566      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25567      * @return {Form} this
25568      */
25569     render : function(ct)
25570     {
25571         
25572         
25573         
25574         ct = Roo.get(ct);
25575         var o = this.autoCreate || {
25576             tag: 'form',
25577             method : this.method || 'POST',
25578             id : this.id || Roo.id()
25579         };
25580         this.initEl(ct.createChild(o));
25581
25582         this.root.render(this.el);
25583         
25584        
25585              
25586         this.items.each(function(f){
25587             f.render('x-form-el-'+f.id);
25588         });
25589
25590         if(this.buttons.length > 0){
25591             // tables are required to maintain order and for correct IE layout
25592             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25593                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25594                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25595             }}, null, true);
25596             var tr = tb.getElementsByTagName('tr')[0];
25597             for(var i = 0, len = this.buttons.length; i < len; i++) {
25598                 var b = this.buttons[i];
25599                 var td = document.createElement('td');
25600                 td.className = 'x-form-btn-td';
25601                 b.render(tr.appendChild(td));
25602             }
25603         }
25604         if(this.monitorValid){ // initialize after render
25605             this.startMonitoring();
25606         }
25607         this.fireEvent('rendered', this);
25608         return this;
25609     },
25610
25611     /**
25612      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25613      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25614      * object or a valid Roo.DomHelper element config
25615      * @param {Function} handler The function called when the button is clicked
25616      * @param {Object} scope (optional) The scope of the handler function
25617      * @return {Roo.Button}
25618      */
25619     addButton : function(config, handler, scope){
25620         var bc = {
25621             handler: handler,
25622             scope: scope,
25623             minWidth: this.minButtonWidth,
25624             hideParent:true
25625         };
25626         if(typeof config == "string"){
25627             bc.text = config;
25628         }else{
25629             Roo.apply(bc, config);
25630         }
25631         var btn = new Roo.Button(null, bc);
25632         this.buttons.push(btn);
25633         return btn;
25634     },
25635
25636      /**
25637      * Adds a series of form elements (using the xtype property as the factory method.
25638      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25639      * @param {Object} config 
25640      */
25641     
25642     addxtype : function()
25643     {
25644         var ar = Array.prototype.slice.call(arguments, 0);
25645         var ret = false;
25646         for(var i = 0; i < ar.length; i++) {
25647             if (!ar[i]) {
25648                 continue; // skip -- if this happends something invalid got sent, we 
25649                 // should ignore it, as basically that interface element will not show up
25650                 // and that should be pretty obvious!!
25651             }
25652             
25653             if (Roo.form[ar[i].xtype]) {
25654                 ar[i].form = this;
25655                 var fe = Roo.factory(ar[i], Roo.form);
25656                 if (!ret) {
25657                     ret = fe;
25658                 }
25659                 fe.form = this;
25660                 if (fe.store) {
25661                     fe.store.form = this;
25662                 }
25663                 if (fe.isLayout) {  
25664                          
25665                     this.start(fe);
25666                     this.allItems.push(fe);
25667                     if (fe.items && fe.addxtype) {
25668                         fe.addxtype.apply(fe, fe.items);
25669                         delete fe.items;
25670                     }
25671                      this.end();
25672                     continue;
25673                 }
25674                 
25675                 
25676                  
25677                 this.add(fe);
25678               //  console.log('adding ' + ar[i].xtype);
25679             }
25680             if (ar[i].xtype == 'Button') {  
25681                 //console.log('adding button');
25682                 //console.log(ar[i]);
25683                 this.addButton(ar[i]);
25684                 this.allItems.push(fe);
25685                 continue;
25686             }
25687             
25688             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25689                 alert('end is not supported on xtype any more, use items');
25690             //    this.end();
25691             //    //console.log('adding end');
25692             }
25693             
25694         }
25695         return ret;
25696     },
25697     
25698     /**
25699      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25700      * option "monitorValid"
25701      */
25702     startMonitoring : function(){
25703         if(!this.bound){
25704             this.bound = true;
25705             Roo.TaskMgr.start({
25706                 run : this.bindHandler,
25707                 interval : this.monitorPoll || 200,
25708                 scope: this
25709             });
25710         }
25711     },
25712
25713     /**
25714      * Stops monitoring of the valid state of this form
25715      */
25716     stopMonitoring : function(){
25717         this.bound = false;
25718     },
25719
25720     // private
25721     bindHandler : function(){
25722         if(!this.bound){
25723             return false; // stops binding
25724         }
25725         var valid = true;
25726         this.items.each(function(f){
25727             if(!f.isValid(true)){
25728                 valid = false;
25729                 return false;
25730             }
25731         });
25732         for(var i = 0, len = this.buttons.length; i < len; i++){
25733             var btn = this.buttons[i];
25734             if(btn.formBind === true && btn.disabled === valid){
25735                 btn.setDisabled(!valid);
25736             }
25737         }
25738         this.fireEvent('clientvalidation', this, valid);
25739     }
25740     
25741     
25742     
25743     
25744     
25745     
25746     
25747     
25748 });
25749
25750
25751 // back compat
25752 Roo.Form = Roo.form.Form;
25753 /*
25754  * Based on:
25755  * Ext JS Library 1.1.1
25756  * Copyright(c) 2006-2007, Ext JS, LLC.
25757  *
25758  * Originally Released Under LGPL - original licence link has changed is not relivant.
25759  *
25760  * Fork - LGPL
25761  * <script type="text/javascript">
25762  */
25763
25764 // as we use this in bootstrap.
25765 Roo.namespace('Roo.form');
25766  /**
25767  * @class Roo.form.Action
25768  * Internal Class used to handle form actions
25769  * @constructor
25770  * @param {Roo.form.BasicForm} el The form element or its id
25771  * @param {Object} config Configuration options
25772  */
25773
25774  
25775  
25776 // define the action interface
25777 Roo.form.Action = function(form, options){
25778     this.form = form;
25779     this.options = options || {};
25780 };
25781 /**
25782  * Client Validation Failed
25783  * @const 
25784  */
25785 Roo.form.Action.CLIENT_INVALID = 'client';
25786 /**
25787  * Server Validation Failed
25788  * @const 
25789  */
25790 Roo.form.Action.SERVER_INVALID = 'server';
25791  /**
25792  * Connect to Server Failed
25793  * @const 
25794  */
25795 Roo.form.Action.CONNECT_FAILURE = 'connect';
25796 /**
25797  * Reading Data from Server Failed
25798  * @const 
25799  */
25800 Roo.form.Action.LOAD_FAILURE = 'load';
25801
25802 Roo.form.Action.prototype = {
25803     type : 'default',
25804     failureType : undefined,
25805     response : undefined,
25806     result : undefined,
25807
25808     // interface method
25809     run : function(options){
25810
25811     },
25812
25813     // interface method
25814     success : function(response){
25815
25816     },
25817
25818     // interface method
25819     handleResponse : function(response){
25820
25821     },
25822
25823     // default connection failure
25824     failure : function(response){
25825         
25826         this.response = response;
25827         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25828         this.form.afterAction(this, false);
25829     },
25830
25831     processResponse : function(response){
25832         this.response = response;
25833         if(!response.responseText){
25834             return true;
25835         }
25836         this.result = this.handleResponse(response);
25837         return this.result;
25838     },
25839
25840     // utility functions used internally
25841     getUrl : function(appendParams){
25842         var url = this.options.url || this.form.url || this.form.el.dom.action;
25843         if(appendParams){
25844             var p = this.getParams();
25845             if(p){
25846                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25847             }
25848         }
25849         return url;
25850     },
25851
25852     getMethod : function(){
25853         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25854     },
25855
25856     getParams : function(){
25857         var bp = this.form.baseParams;
25858         var p = this.options.params;
25859         if(p){
25860             if(typeof p == "object"){
25861                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25862             }else if(typeof p == 'string' && bp){
25863                 p += '&' + Roo.urlEncode(bp);
25864             }
25865         }else if(bp){
25866             p = Roo.urlEncode(bp);
25867         }
25868         return p;
25869     },
25870
25871     createCallback : function(){
25872         return {
25873             success: this.success,
25874             failure: this.failure,
25875             scope: this,
25876             timeout: (this.form.timeout*1000),
25877             upload: this.form.fileUpload ? this.success : undefined
25878         };
25879     }
25880 };
25881
25882 Roo.form.Action.Submit = function(form, options){
25883     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25884 };
25885
25886 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25887     type : 'submit',
25888
25889     haveProgress : false,
25890     uploadComplete : false,
25891     
25892     // uploadProgress indicator.
25893     uploadProgress : function()
25894     {
25895         if (!this.form.progressUrl) {
25896             return;
25897         }
25898         
25899         if (!this.haveProgress) {
25900             Roo.MessageBox.progress("Uploading", "Uploading");
25901         }
25902         if (this.uploadComplete) {
25903            Roo.MessageBox.hide();
25904            return;
25905         }
25906         
25907         this.haveProgress = true;
25908    
25909         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25910         
25911         var c = new Roo.data.Connection();
25912         c.request({
25913             url : this.form.progressUrl,
25914             params: {
25915                 id : uid
25916             },
25917             method: 'GET',
25918             success : function(req){
25919                //console.log(data);
25920                 var rdata = false;
25921                 var edata;
25922                 try  {
25923                    rdata = Roo.decode(req.responseText)
25924                 } catch (e) {
25925                     Roo.log("Invalid data from server..");
25926                     Roo.log(edata);
25927                     return;
25928                 }
25929                 if (!rdata || !rdata.success) {
25930                     Roo.log(rdata);
25931                     Roo.MessageBox.alert(Roo.encode(rdata));
25932                     return;
25933                 }
25934                 var data = rdata.data;
25935                 
25936                 if (this.uploadComplete) {
25937                    Roo.MessageBox.hide();
25938                    return;
25939                 }
25940                    
25941                 if (data){
25942                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25943                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25944                     );
25945                 }
25946                 this.uploadProgress.defer(2000,this);
25947             },
25948        
25949             failure: function(data) {
25950                 Roo.log('progress url failed ');
25951                 Roo.log(data);
25952             },
25953             scope : this
25954         });
25955            
25956     },
25957     
25958     
25959     run : function()
25960     {
25961         // run get Values on the form, so it syncs any secondary forms.
25962         this.form.getValues();
25963         
25964         var o = this.options;
25965         var method = this.getMethod();
25966         var isPost = method == 'POST';
25967         if(o.clientValidation === false || this.form.isValid()){
25968             
25969             if (this.form.progressUrl) {
25970                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25971                     (new Date() * 1) + '' + Math.random());
25972                     
25973             } 
25974             
25975             
25976             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25977                 form:this.form.el.dom,
25978                 url:this.getUrl(!isPost),
25979                 method: method,
25980                 params:isPost ? this.getParams() : null,
25981                 isUpload: this.form.fileUpload,
25982                 formData : this.form.formData
25983             }));
25984             
25985             this.uploadProgress();
25986
25987         }else if (o.clientValidation !== false){ // client validation failed
25988             this.failureType = Roo.form.Action.CLIENT_INVALID;
25989             this.form.afterAction(this, false);
25990         }
25991     },
25992
25993     success : function(response)
25994     {
25995         this.uploadComplete= true;
25996         if (this.haveProgress) {
25997             Roo.MessageBox.hide();
25998         }
25999         
26000         
26001         var result = this.processResponse(response);
26002         if(result === true || result.success){
26003             this.form.afterAction(this, true);
26004             return;
26005         }
26006         if(result.errors){
26007             this.form.markInvalid(result.errors);
26008             this.failureType = Roo.form.Action.SERVER_INVALID;
26009         }
26010         this.form.afterAction(this, false);
26011     },
26012     failure : function(response)
26013     {
26014         this.uploadComplete= true;
26015         if (this.haveProgress) {
26016             Roo.MessageBox.hide();
26017         }
26018         
26019         this.response = response;
26020         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26021         this.form.afterAction(this, false);
26022     },
26023     
26024     handleResponse : function(response){
26025         if(this.form.errorReader){
26026             var rs = this.form.errorReader.read(response);
26027             var errors = [];
26028             if(rs.records){
26029                 for(var i = 0, len = rs.records.length; i < len; i++) {
26030                     var r = rs.records[i];
26031                     errors[i] = r.data;
26032                 }
26033             }
26034             if(errors.length < 1){
26035                 errors = null;
26036             }
26037             return {
26038                 success : rs.success,
26039                 errors : errors
26040             };
26041         }
26042         var ret = false;
26043         try {
26044             ret = Roo.decode(response.responseText);
26045         } catch (e) {
26046             ret = {
26047                 success: false,
26048                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26049                 errors : []
26050             };
26051         }
26052         return ret;
26053         
26054     }
26055 });
26056
26057
26058 Roo.form.Action.Load = function(form, options){
26059     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26060     this.reader = this.form.reader;
26061 };
26062
26063 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26064     type : 'load',
26065
26066     run : function(){
26067         
26068         Roo.Ajax.request(Roo.apply(
26069                 this.createCallback(), {
26070                     method:this.getMethod(),
26071                     url:this.getUrl(false),
26072                     params:this.getParams()
26073         }));
26074     },
26075
26076     success : function(response){
26077         
26078         var result = this.processResponse(response);
26079         if(result === true || !result.success || !result.data){
26080             this.failureType = Roo.form.Action.LOAD_FAILURE;
26081             this.form.afterAction(this, false);
26082             return;
26083         }
26084         this.form.clearInvalid();
26085         this.form.setValues(result.data);
26086         this.form.afterAction(this, true);
26087     },
26088
26089     handleResponse : function(response){
26090         if(this.form.reader){
26091             var rs = this.form.reader.read(response);
26092             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26093             return {
26094                 success : rs.success,
26095                 data : data
26096             };
26097         }
26098         return Roo.decode(response.responseText);
26099     }
26100 });
26101
26102 Roo.form.Action.ACTION_TYPES = {
26103     'load' : Roo.form.Action.Load,
26104     'submit' : Roo.form.Action.Submit
26105 };/*
26106  * Based on:
26107  * Ext JS Library 1.1.1
26108  * Copyright(c) 2006-2007, Ext JS, LLC.
26109  *
26110  * Originally Released Under LGPL - original licence link has changed is not relivant.
26111  *
26112  * Fork - LGPL
26113  * <script type="text/javascript">
26114  */
26115  
26116 /**
26117  * @class Roo.form.Layout
26118  * @extends Roo.Component
26119  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26120  * @constructor
26121  * @param {Object} config Configuration options
26122  */
26123 Roo.form.Layout = function(config){
26124     var xitems = [];
26125     if (config.items) {
26126         xitems = config.items;
26127         delete config.items;
26128     }
26129     Roo.form.Layout.superclass.constructor.call(this, config);
26130     this.stack = [];
26131     Roo.each(xitems, this.addxtype, this);
26132      
26133 };
26134
26135 Roo.extend(Roo.form.Layout, Roo.Component, {
26136     /**
26137      * @cfg {String/Object} autoCreate
26138      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26139      */
26140     /**
26141      * @cfg {String/Object/Function} style
26142      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26143      * a function which returns such a specification.
26144      */
26145     /**
26146      * @cfg {String} labelAlign
26147      * Valid values are "left," "top" and "right" (defaults to "left")
26148      */
26149     /**
26150      * @cfg {Number} labelWidth
26151      * Fixed width in pixels of all field labels (defaults to undefined)
26152      */
26153     /**
26154      * @cfg {Boolean} clear
26155      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26156      */
26157     clear : true,
26158     /**
26159      * @cfg {String} labelSeparator
26160      * The separator to use after field labels (defaults to ':')
26161      */
26162     labelSeparator : ':',
26163     /**
26164      * @cfg {Boolean} hideLabels
26165      * True to suppress the display of field labels in this layout (defaults to false)
26166      */
26167     hideLabels : false,
26168
26169     // private
26170     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26171     
26172     isLayout : true,
26173     
26174     // private
26175     onRender : function(ct, position){
26176         if(this.el){ // from markup
26177             this.el = Roo.get(this.el);
26178         }else {  // generate
26179             var cfg = this.getAutoCreate();
26180             this.el = ct.createChild(cfg, position);
26181         }
26182         if(this.style){
26183             this.el.applyStyles(this.style);
26184         }
26185         if(this.labelAlign){
26186             this.el.addClass('x-form-label-'+this.labelAlign);
26187         }
26188         if(this.hideLabels){
26189             this.labelStyle = "display:none";
26190             this.elementStyle = "padding-left:0;";
26191         }else{
26192             if(typeof this.labelWidth == 'number'){
26193                 this.labelStyle = "width:"+this.labelWidth+"px;";
26194                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26195             }
26196             if(this.labelAlign == 'top'){
26197                 this.labelStyle = "width:auto;";
26198                 this.elementStyle = "padding-left:0;";
26199             }
26200         }
26201         var stack = this.stack;
26202         var slen = stack.length;
26203         if(slen > 0){
26204             if(!this.fieldTpl){
26205                 var t = new Roo.Template(
26206                     '<div class="x-form-item {5}">',
26207                         '<label for="{0}" style="{2}">{1}{4}</label>',
26208                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26209                         '</div>',
26210                     '</div><div class="x-form-clear-left"></div>'
26211                 );
26212                 t.disableFormats = true;
26213                 t.compile();
26214                 Roo.form.Layout.prototype.fieldTpl = t;
26215             }
26216             for(var i = 0; i < slen; i++) {
26217                 if(stack[i].isFormField){
26218                     this.renderField(stack[i]);
26219                 }else{
26220                     this.renderComponent(stack[i]);
26221                 }
26222             }
26223         }
26224         if(this.clear){
26225             this.el.createChild({cls:'x-form-clear'});
26226         }
26227     },
26228
26229     // private
26230     renderField : function(f){
26231         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26232                f.id, //0
26233                f.fieldLabel, //1
26234                f.labelStyle||this.labelStyle||'', //2
26235                this.elementStyle||'', //3
26236                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26237                f.itemCls||this.itemCls||''  //5
26238        ], true).getPrevSibling());
26239     },
26240
26241     // private
26242     renderComponent : function(c){
26243         c.render(c.isLayout ? this.el : this.el.createChild());    
26244     },
26245     /**
26246      * Adds a object form elements (using the xtype property as the factory method.)
26247      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26248      * @param {Object} config 
26249      */
26250     addxtype : function(o)
26251     {
26252         // create the lement.
26253         o.form = this.form;
26254         var fe = Roo.factory(o, Roo.form);
26255         this.form.allItems.push(fe);
26256         this.stack.push(fe);
26257         
26258         if (fe.isFormField) {
26259             this.form.items.add(fe);
26260         }
26261          
26262         return fe;
26263     }
26264 });
26265
26266 /**
26267  * @class Roo.form.Column
26268  * @extends Roo.form.Layout
26269  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26270  * @constructor
26271  * @param {Object} config Configuration options
26272  */
26273 Roo.form.Column = function(config){
26274     Roo.form.Column.superclass.constructor.call(this, config);
26275 };
26276
26277 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26278     /**
26279      * @cfg {Number/String} width
26280      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26281      */
26282     /**
26283      * @cfg {String/Object} autoCreate
26284      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26285      */
26286
26287     // private
26288     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26289
26290     // private
26291     onRender : function(ct, position){
26292         Roo.form.Column.superclass.onRender.call(this, ct, position);
26293         if(this.width){
26294             this.el.setWidth(this.width);
26295         }
26296     }
26297 });
26298
26299
26300 /**
26301  * @class Roo.form.Row
26302  * @extends Roo.form.Layout
26303  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26304  * @constructor
26305  * @param {Object} config Configuration options
26306  */
26307
26308  
26309 Roo.form.Row = function(config){
26310     Roo.form.Row.superclass.constructor.call(this, config);
26311 };
26312  
26313 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26314       /**
26315      * @cfg {Number/String} width
26316      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26317      */
26318     /**
26319      * @cfg {Number/String} height
26320      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26321      */
26322     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26323     
26324     padWidth : 20,
26325     // private
26326     onRender : function(ct, position){
26327         //console.log('row render');
26328         if(!this.rowTpl){
26329             var t = new Roo.Template(
26330                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26331                     '<label for="{0}" style="{2}">{1}{4}</label>',
26332                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26333                     '</div>',
26334                 '</div>'
26335             );
26336             t.disableFormats = true;
26337             t.compile();
26338             Roo.form.Layout.prototype.rowTpl = t;
26339         }
26340         this.fieldTpl = this.rowTpl;
26341         
26342         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26343         var labelWidth = 100;
26344         
26345         if ((this.labelAlign != 'top')) {
26346             if (typeof this.labelWidth == 'number') {
26347                 labelWidth = this.labelWidth
26348             }
26349             this.padWidth =  20 + labelWidth;
26350             
26351         }
26352         
26353         Roo.form.Column.superclass.onRender.call(this, ct, position);
26354         if(this.width){
26355             this.el.setWidth(this.width);
26356         }
26357         if(this.height){
26358             this.el.setHeight(this.height);
26359         }
26360     },
26361     
26362     // private
26363     renderField : function(f){
26364         f.fieldEl = this.fieldTpl.append(this.el, [
26365                f.id, f.fieldLabel,
26366                f.labelStyle||this.labelStyle||'',
26367                this.elementStyle||'',
26368                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26369                f.itemCls||this.itemCls||'',
26370                f.width ? f.width + this.padWidth : 160 + this.padWidth
26371        ],true);
26372     }
26373 });
26374  
26375
26376 /**
26377  * @class Roo.form.FieldSet
26378  * @extends Roo.form.Layout
26379  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26380  * @constructor
26381  * @param {Object} config Configuration options
26382  */
26383 Roo.form.FieldSet = function(config){
26384     Roo.form.FieldSet.superclass.constructor.call(this, config);
26385 };
26386
26387 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26388     /**
26389      * @cfg {String} legend
26390      * The text to display as the legend for the FieldSet (defaults to '')
26391      */
26392     /**
26393      * @cfg {String/Object} autoCreate
26394      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26395      */
26396
26397     // private
26398     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26399
26400     // private
26401     onRender : function(ct, position){
26402         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26403         if(this.legend){
26404             this.setLegend(this.legend);
26405         }
26406     },
26407
26408     // private
26409     setLegend : function(text){
26410         if(this.rendered){
26411             this.el.child('legend').update(text);
26412         }
26413     }
26414 });/*
26415  * Based on:
26416  * Ext JS Library 1.1.1
26417  * Copyright(c) 2006-2007, Ext JS, LLC.
26418  *
26419  * Originally Released Under LGPL - original licence link has changed is not relivant.
26420  *
26421  * Fork - LGPL
26422  * <script type="text/javascript">
26423  */
26424 /**
26425  * @class Roo.form.VTypes
26426  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26427  * @singleton
26428  */
26429 Roo.form.VTypes = function(){
26430     // closure these in so they are only created once.
26431     var alpha = /^[a-zA-Z_]+$/;
26432     var alphanum = /^[a-zA-Z0-9_]+$/;
26433     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26434     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26435
26436     // All these messages and functions are configurable
26437     return {
26438         /**
26439          * The function used to validate email addresses
26440          * @param {String} value The email address
26441          */
26442         'email' : function(v){
26443             return email.test(v);
26444         },
26445         /**
26446          * The error text to display when the email validation function returns false
26447          * @type String
26448          */
26449         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26450         /**
26451          * The keystroke filter mask to be applied on email input
26452          * @type RegExp
26453          */
26454         'emailMask' : /[a-z0-9_\.\-@]/i,
26455
26456         /**
26457          * The function used to validate URLs
26458          * @param {String} value The URL
26459          */
26460         'url' : function(v){
26461             return url.test(v);
26462         },
26463         /**
26464          * The error text to display when the url validation function returns false
26465          * @type String
26466          */
26467         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26468         
26469         /**
26470          * The function used to validate alpha values
26471          * @param {String} value The value
26472          */
26473         'alpha' : function(v){
26474             return alpha.test(v);
26475         },
26476         /**
26477          * The error text to display when the alpha validation function returns false
26478          * @type String
26479          */
26480         'alphaText' : 'This field should only contain letters and _',
26481         /**
26482          * The keystroke filter mask to be applied on alpha input
26483          * @type RegExp
26484          */
26485         'alphaMask' : /[a-z_]/i,
26486
26487         /**
26488          * The function used to validate alphanumeric values
26489          * @param {String} value The value
26490          */
26491         'alphanum' : function(v){
26492             return alphanum.test(v);
26493         },
26494         /**
26495          * The error text to display when the alphanumeric validation function returns false
26496          * @type String
26497          */
26498         'alphanumText' : 'This field should only contain letters, numbers and _',
26499         /**
26500          * The keystroke filter mask to be applied on alphanumeric input
26501          * @type RegExp
26502          */
26503         'alphanumMask' : /[a-z0-9_]/i
26504     };
26505 }();//<script type="text/javascript">
26506
26507 /**
26508  * @class Roo.form.FCKeditor
26509  * @extends Roo.form.TextArea
26510  * Wrapper around the FCKEditor http://www.fckeditor.net
26511  * @constructor
26512  * Creates a new FCKeditor
26513  * @param {Object} config Configuration options
26514  */
26515 Roo.form.FCKeditor = function(config){
26516     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26517     this.addEvents({
26518          /**
26519          * @event editorinit
26520          * Fired when the editor is initialized - you can add extra handlers here..
26521          * @param {FCKeditor} this
26522          * @param {Object} the FCK object.
26523          */
26524         editorinit : true
26525     });
26526     
26527     
26528 };
26529 Roo.form.FCKeditor.editors = { };
26530 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26531 {
26532     //defaultAutoCreate : {
26533     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26534     //},
26535     // private
26536     /**
26537      * @cfg {Object} fck options - see fck manual for details.
26538      */
26539     fckconfig : false,
26540     
26541     /**
26542      * @cfg {Object} fck toolbar set (Basic or Default)
26543      */
26544     toolbarSet : 'Basic',
26545     /**
26546      * @cfg {Object} fck BasePath
26547      */ 
26548     basePath : '/fckeditor/',
26549     
26550     
26551     frame : false,
26552     
26553     value : '',
26554     
26555    
26556     onRender : function(ct, position)
26557     {
26558         if(!this.el){
26559             this.defaultAutoCreate = {
26560                 tag: "textarea",
26561                 style:"width:300px;height:60px;",
26562                 autocomplete: "new-password"
26563             };
26564         }
26565         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26566         /*
26567         if(this.grow){
26568             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26569             if(this.preventScrollbars){
26570                 this.el.setStyle("overflow", "hidden");
26571             }
26572             this.el.setHeight(this.growMin);
26573         }
26574         */
26575         //console.log('onrender' + this.getId() );
26576         Roo.form.FCKeditor.editors[this.getId()] = this;
26577          
26578
26579         this.replaceTextarea() ;
26580         
26581     },
26582     
26583     getEditor : function() {
26584         return this.fckEditor;
26585     },
26586     /**
26587      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26588      * @param {Mixed} value The value to set
26589      */
26590     
26591     
26592     setValue : function(value)
26593     {
26594         //console.log('setValue: ' + value);
26595         
26596         if(typeof(value) == 'undefined') { // not sure why this is happending...
26597             return;
26598         }
26599         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26600         
26601         //if(!this.el || !this.getEditor()) {
26602         //    this.value = value;
26603             //this.setValue.defer(100,this,[value]);    
26604         //    return;
26605         //} 
26606         
26607         if(!this.getEditor()) {
26608             return;
26609         }
26610         
26611         this.getEditor().SetData(value);
26612         
26613         //
26614
26615     },
26616
26617     /**
26618      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26619      * @return {Mixed} value The field value
26620      */
26621     getValue : function()
26622     {
26623         
26624         if (this.frame && this.frame.dom.style.display == 'none') {
26625             return Roo.form.FCKeditor.superclass.getValue.call(this);
26626         }
26627         
26628         if(!this.el || !this.getEditor()) {
26629            
26630            // this.getValue.defer(100,this); 
26631             return this.value;
26632         }
26633        
26634         
26635         var value=this.getEditor().GetData();
26636         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26637         return Roo.form.FCKeditor.superclass.getValue.call(this);
26638         
26639
26640     },
26641
26642     /**
26643      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26644      * @return {Mixed} value The field value
26645      */
26646     getRawValue : function()
26647     {
26648         if (this.frame && this.frame.dom.style.display == 'none') {
26649             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26650         }
26651         
26652         if(!this.el || !this.getEditor()) {
26653             //this.getRawValue.defer(100,this); 
26654             return this.value;
26655             return;
26656         }
26657         
26658         
26659         
26660         var value=this.getEditor().GetData();
26661         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26662         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26663          
26664     },
26665     
26666     setSize : function(w,h) {
26667         
26668         
26669         
26670         //if (this.frame && this.frame.dom.style.display == 'none') {
26671         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26672         //    return;
26673         //}
26674         //if(!this.el || !this.getEditor()) {
26675         //    this.setSize.defer(100,this, [w,h]); 
26676         //    return;
26677         //}
26678         
26679         
26680         
26681         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26682         
26683         this.frame.dom.setAttribute('width', w);
26684         this.frame.dom.setAttribute('height', h);
26685         this.frame.setSize(w,h);
26686         
26687     },
26688     
26689     toggleSourceEdit : function(value) {
26690         
26691       
26692          
26693         this.el.dom.style.display = value ? '' : 'none';
26694         this.frame.dom.style.display = value ?  'none' : '';
26695         
26696     },
26697     
26698     
26699     focus: function(tag)
26700     {
26701         if (this.frame.dom.style.display == 'none') {
26702             return Roo.form.FCKeditor.superclass.focus.call(this);
26703         }
26704         if(!this.el || !this.getEditor()) {
26705             this.focus.defer(100,this, [tag]); 
26706             return;
26707         }
26708         
26709         
26710         
26711         
26712         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26713         this.getEditor().Focus();
26714         if (tgs.length) {
26715             if (!this.getEditor().Selection.GetSelection()) {
26716                 this.focus.defer(100,this, [tag]); 
26717                 return;
26718             }
26719             
26720             
26721             var r = this.getEditor().EditorDocument.createRange();
26722             r.setStart(tgs[0],0);
26723             r.setEnd(tgs[0],0);
26724             this.getEditor().Selection.GetSelection().removeAllRanges();
26725             this.getEditor().Selection.GetSelection().addRange(r);
26726             this.getEditor().Focus();
26727         }
26728         
26729     },
26730     
26731     
26732     
26733     replaceTextarea : function()
26734     {
26735         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26736             return ;
26737         }
26738         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26739         //{
26740             // We must check the elements firstly using the Id and then the name.
26741         var oTextarea = document.getElementById( this.getId() );
26742         
26743         var colElementsByName = document.getElementsByName( this.getId() ) ;
26744          
26745         oTextarea.style.display = 'none' ;
26746
26747         if ( oTextarea.tabIndex ) {            
26748             this.TabIndex = oTextarea.tabIndex ;
26749         }
26750         
26751         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26752         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26753         this.frame = Roo.get(this.getId() + '___Frame')
26754     },
26755     
26756     _getConfigHtml : function()
26757     {
26758         var sConfig = '' ;
26759
26760         for ( var o in this.fckconfig ) {
26761             sConfig += sConfig.length > 0  ? '&amp;' : '';
26762             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26763         }
26764
26765         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26766     },
26767     
26768     
26769     _getIFrameHtml : function()
26770     {
26771         var sFile = 'fckeditor.html' ;
26772         /* no idea what this is about..
26773         try
26774         {
26775             if ( (/fcksource=true/i).test( window.top.location.search ) )
26776                 sFile = 'fckeditor.original.html' ;
26777         }
26778         catch (e) { 
26779         */
26780
26781         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26782         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26783         
26784         
26785         var html = '<iframe id="' + this.getId() +
26786             '___Frame" src="' + sLink +
26787             '" width="' + this.width +
26788             '" height="' + this.height + '"' +
26789             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26790             ' frameborder="0" scrolling="no"></iframe>' ;
26791
26792         return html ;
26793     },
26794     
26795     _insertHtmlBefore : function( html, element )
26796     {
26797         if ( element.insertAdjacentHTML )       {
26798             // IE
26799             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26800         } else { // Gecko
26801             var oRange = document.createRange() ;
26802             oRange.setStartBefore( element ) ;
26803             var oFragment = oRange.createContextualFragment( html );
26804             element.parentNode.insertBefore( oFragment, element ) ;
26805         }
26806     }
26807     
26808     
26809   
26810     
26811     
26812     
26813     
26814
26815 });
26816
26817 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26818
26819 function FCKeditor_OnComplete(editorInstance){
26820     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26821     f.fckEditor = editorInstance;
26822     //console.log("loaded");
26823     f.fireEvent('editorinit', f, editorInstance);
26824
26825   
26826
26827  
26828
26829
26830
26831
26832
26833
26834
26835
26836
26837
26838
26839
26840
26841
26842
26843 //<script type="text/javascript">
26844 /**
26845  * @class Roo.form.GridField
26846  * @extends Roo.form.Field
26847  * Embed a grid (or editable grid into a form)
26848  * STATUS ALPHA
26849  * 
26850  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26851  * it needs 
26852  * xgrid.store = Roo.data.Store
26853  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26854  * xgrid.store.reader = Roo.data.JsonReader 
26855  * 
26856  * 
26857  * @constructor
26858  * Creates a new GridField
26859  * @param {Object} config Configuration options
26860  */
26861 Roo.form.GridField = function(config){
26862     Roo.form.GridField.superclass.constructor.call(this, config);
26863      
26864 };
26865
26866 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26867     /**
26868      * @cfg {Number} width  - used to restrict width of grid..
26869      */
26870     width : 100,
26871     /**
26872      * @cfg {Number} height - used to restrict height of grid..
26873      */
26874     height : 50,
26875      /**
26876      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26877          * 
26878          *}
26879      */
26880     xgrid : false, 
26881     /**
26882      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26883      * {tag: "input", type: "checkbox", autocomplete: "off"})
26884      */
26885    // defaultAutoCreate : { tag: 'div' },
26886     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26887     /**
26888      * @cfg {String} addTitle Text to include for adding a title.
26889      */
26890     addTitle : false,
26891     //
26892     onResize : function(){
26893         Roo.form.Field.superclass.onResize.apply(this, arguments);
26894     },
26895
26896     initEvents : function(){
26897         // Roo.form.Checkbox.superclass.initEvents.call(this);
26898         // has no events...
26899        
26900     },
26901
26902
26903     getResizeEl : function(){
26904         return this.wrap;
26905     },
26906
26907     getPositionEl : function(){
26908         return this.wrap;
26909     },
26910
26911     // private
26912     onRender : function(ct, position){
26913         
26914         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26915         var style = this.style;
26916         delete this.style;
26917         
26918         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26919         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26920         this.viewEl = this.wrap.createChild({ tag: 'div' });
26921         if (style) {
26922             this.viewEl.applyStyles(style);
26923         }
26924         if (this.width) {
26925             this.viewEl.setWidth(this.width);
26926         }
26927         if (this.height) {
26928             this.viewEl.setHeight(this.height);
26929         }
26930         //if(this.inputValue !== undefined){
26931         //this.setValue(this.value);
26932         
26933         
26934         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26935         
26936         
26937         this.grid.render();
26938         this.grid.getDataSource().on('remove', this.refreshValue, this);
26939         this.grid.getDataSource().on('update', this.refreshValue, this);
26940         this.grid.on('afteredit', this.refreshValue, this);
26941  
26942     },
26943      
26944     
26945     /**
26946      * Sets the value of the item. 
26947      * @param {String} either an object  or a string..
26948      */
26949     setValue : function(v){
26950         //this.value = v;
26951         v = v || []; // empty set..
26952         // this does not seem smart - it really only affects memoryproxy grids..
26953         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26954             var ds = this.grid.getDataSource();
26955             // assumes a json reader..
26956             var data = {}
26957             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26958             ds.loadData( data);
26959         }
26960         // clear selection so it does not get stale.
26961         if (this.grid.sm) { 
26962             this.grid.sm.clearSelections();
26963         }
26964         
26965         Roo.form.GridField.superclass.setValue.call(this, v);
26966         this.refreshValue();
26967         // should load data in the grid really....
26968     },
26969     
26970     // private
26971     refreshValue: function() {
26972          var val = [];
26973         this.grid.getDataSource().each(function(r) {
26974             val.push(r.data);
26975         });
26976         this.el.dom.value = Roo.encode(val);
26977     }
26978     
26979      
26980     
26981     
26982 });/*
26983  * Based on:
26984  * Ext JS Library 1.1.1
26985  * Copyright(c) 2006-2007, Ext JS, LLC.
26986  *
26987  * Originally Released Under LGPL - original licence link has changed is not relivant.
26988  *
26989  * Fork - LGPL
26990  * <script type="text/javascript">
26991  */
26992 /**
26993  * @class Roo.form.DisplayField
26994  * @extends Roo.form.Field
26995  * A generic Field to display non-editable data.
26996  * @cfg {Boolean} closable (true|false) default false
26997  * @constructor
26998  * Creates a new Display Field item.
26999  * @param {Object} config Configuration options
27000  */
27001 Roo.form.DisplayField = function(config){
27002     Roo.form.DisplayField.superclass.constructor.call(this, config);
27003     
27004     this.addEvents({
27005         /**
27006          * @event close
27007          * Fires after the click the close btn
27008              * @param {Roo.form.DisplayField} this
27009              */
27010         close : true
27011     });
27012 };
27013
27014 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27015     inputType:      'hidden',
27016     allowBlank:     true,
27017     readOnly:         true,
27018     
27019  
27020     /**
27021      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27022      */
27023     focusClass : undefined,
27024     /**
27025      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27026      */
27027     fieldClass: 'x-form-field',
27028     
27029      /**
27030      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27031      */
27032     valueRenderer: undefined,
27033     
27034     width: 100,
27035     /**
27036      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27037      * {tag: "input", type: "checkbox", autocomplete: "off"})
27038      */
27039      
27040  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27041  
27042     closable : false,
27043     
27044     onResize : function(){
27045         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27046         
27047     },
27048
27049     initEvents : function(){
27050         // Roo.form.Checkbox.superclass.initEvents.call(this);
27051         // has no events...
27052         
27053         if(this.closable){
27054             this.closeEl.on('click', this.onClose, this);
27055         }
27056        
27057     },
27058
27059
27060     getResizeEl : function(){
27061         return this.wrap;
27062     },
27063
27064     getPositionEl : function(){
27065         return this.wrap;
27066     },
27067
27068     // private
27069     onRender : function(ct, position){
27070         
27071         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27072         //if(this.inputValue !== undefined){
27073         this.wrap = this.el.wrap();
27074         
27075         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27076         
27077         if(this.closable){
27078             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27079         }
27080         
27081         if (this.bodyStyle) {
27082             this.viewEl.applyStyles(this.bodyStyle);
27083         }
27084         //this.viewEl.setStyle('padding', '2px');
27085         
27086         this.setValue(this.value);
27087         
27088     },
27089 /*
27090     // private
27091     initValue : Roo.emptyFn,
27092
27093   */
27094
27095         // private
27096     onClick : function(){
27097         
27098     },
27099
27100     /**
27101      * Sets the checked state of the checkbox.
27102      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27103      */
27104     setValue : function(v){
27105         this.value = v;
27106         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27107         // this might be called before we have a dom element..
27108         if (!this.viewEl) {
27109             return;
27110         }
27111         this.viewEl.dom.innerHTML = html;
27112         Roo.form.DisplayField.superclass.setValue.call(this, v);
27113
27114     },
27115     
27116     onClose : function(e)
27117     {
27118         e.preventDefault();
27119         
27120         this.fireEvent('close', this);
27121     }
27122 });/*
27123  * 
27124  * Licence- LGPL
27125  * 
27126  */
27127
27128 /**
27129  * @class Roo.form.DayPicker
27130  * @extends Roo.form.Field
27131  * A Day picker show [M] [T] [W] ....
27132  * @constructor
27133  * Creates a new Day Picker
27134  * @param {Object} config Configuration options
27135  */
27136 Roo.form.DayPicker= function(config){
27137     Roo.form.DayPicker.superclass.constructor.call(this, config);
27138      
27139 };
27140
27141 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27142     /**
27143      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27144      */
27145     focusClass : undefined,
27146     /**
27147      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27148      */
27149     fieldClass: "x-form-field",
27150    
27151     /**
27152      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27153      * {tag: "input", type: "checkbox", autocomplete: "off"})
27154      */
27155     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27156     
27157    
27158     actionMode : 'viewEl', 
27159     //
27160     // private
27161  
27162     inputType : 'hidden',
27163     
27164      
27165     inputElement: false, // real input element?
27166     basedOn: false, // ????
27167     
27168     isFormField: true, // not sure where this is needed!!!!
27169
27170     onResize : function(){
27171         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27172         if(!this.boxLabel){
27173             this.el.alignTo(this.wrap, 'c-c');
27174         }
27175     },
27176
27177     initEvents : function(){
27178         Roo.form.Checkbox.superclass.initEvents.call(this);
27179         this.el.on("click", this.onClick,  this);
27180         this.el.on("change", this.onClick,  this);
27181     },
27182
27183
27184     getResizeEl : function(){
27185         return this.wrap;
27186     },
27187
27188     getPositionEl : function(){
27189         return this.wrap;
27190     },
27191
27192     
27193     // private
27194     onRender : function(ct, position){
27195         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27196        
27197         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27198         
27199         var r1 = '<table><tr>';
27200         var r2 = '<tr class="x-form-daypick-icons">';
27201         for (var i=0; i < 7; i++) {
27202             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27203             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27204         }
27205         
27206         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27207         viewEl.select('img').on('click', this.onClick, this);
27208         this.viewEl = viewEl;   
27209         
27210         
27211         // this will not work on Chrome!!!
27212         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27213         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27214         
27215         
27216           
27217
27218     },
27219
27220     // private
27221     initValue : Roo.emptyFn,
27222
27223     /**
27224      * Returns the checked state of the checkbox.
27225      * @return {Boolean} True if checked, else false
27226      */
27227     getValue : function(){
27228         return this.el.dom.value;
27229         
27230     },
27231
27232         // private
27233     onClick : function(e){ 
27234         //this.setChecked(!this.checked);
27235         Roo.get(e.target).toggleClass('x-menu-item-checked');
27236         this.refreshValue();
27237         //if(this.el.dom.checked != this.checked){
27238         //    this.setValue(this.el.dom.checked);
27239        // }
27240     },
27241     
27242     // private
27243     refreshValue : function()
27244     {
27245         var val = '';
27246         this.viewEl.select('img',true).each(function(e,i,n)  {
27247             val += e.is(".x-menu-item-checked") ? String(n) : '';
27248         });
27249         this.setValue(val, true);
27250     },
27251
27252     /**
27253      * Sets the checked state of the checkbox.
27254      * On is always based on a string comparison between inputValue and the param.
27255      * @param {Boolean/String} value - the value to set 
27256      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27257      */
27258     setValue : function(v,suppressEvent){
27259         if (!this.el.dom) {
27260             return;
27261         }
27262         var old = this.el.dom.value ;
27263         this.el.dom.value = v;
27264         if (suppressEvent) {
27265             return ;
27266         }
27267          
27268         // update display..
27269         this.viewEl.select('img',true).each(function(e,i,n)  {
27270             
27271             var on = e.is(".x-menu-item-checked");
27272             var newv = v.indexOf(String(n)) > -1;
27273             if (on != newv) {
27274                 e.toggleClass('x-menu-item-checked');
27275             }
27276             
27277         });
27278         
27279         
27280         this.fireEvent('change', this, v, old);
27281         
27282         
27283     },
27284    
27285     // handle setting of hidden value by some other method!!?!?
27286     setFromHidden: function()
27287     {
27288         if(!this.el){
27289             return;
27290         }
27291         //console.log("SET FROM HIDDEN");
27292         //alert('setFrom hidden');
27293         this.setValue(this.el.dom.value);
27294     },
27295     
27296     onDestroy : function()
27297     {
27298         if(this.viewEl){
27299             Roo.get(this.viewEl).remove();
27300         }
27301          
27302         Roo.form.DayPicker.superclass.onDestroy.call(this);
27303     }
27304
27305 });/*
27306  * RooJS Library 1.1.1
27307  * Copyright(c) 2008-2011  Alan Knowles
27308  *
27309  * License - LGPL
27310  */
27311  
27312
27313 /**
27314  * @class Roo.form.ComboCheck
27315  * @extends Roo.form.ComboBox
27316  * A combobox for multiple select items.
27317  *
27318  * FIXME - could do with a reset button..
27319  * 
27320  * @constructor
27321  * Create a new ComboCheck
27322  * @param {Object} config Configuration options
27323  */
27324 Roo.form.ComboCheck = function(config){
27325     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27326     // should verify some data...
27327     // like
27328     // hiddenName = required..
27329     // displayField = required
27330     // valudField == required
27331     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27332     var _t = this;
27333     Roo.each(req, function(e) {
27334         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27335             throw "Roo.form.ComboCheck : missing value for: " + e;
27336         }
27337     });
27338     
27339     
27340 };
27341
27342 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27343      
27344      
27345     editable : false,
27346      
27347     selectedClass: 'x-menu-item-checked', 
27348     
27349     // private
27350     onRender : function(ct, position){
27351         var _t = this;
27352         
27353         
27354         
27355         if(!this.tpl){
27356             var cls = 'x-combo-list';
27357
27358             
27359             this.tpl =  new Roo.Template({
27360                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27361                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27362                    '<span>{' + this.displayField + '}</span>' +
27363                     '</div>' 
27364                 
27365             });
27366         }
27367  
27368         
27369         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27370         this.view.singleSelect = false;
27371         this.view.multiSelect = true;
27372         this.view.toggleSelect = true;
27373         this.pageTb.add(new Roo.Toolbar.Fill(), {
27374             
27375             text: 'Done',
27376             handler: function()
27377             {
27378                 _t.collapse();
27379             }
27380         });
27381     },
27382     
27383     onViewOver : function(e, t){
27384         // do nothing...
27385         return;
27386         
27387     },
27388     
27389     onViewClick : function(doFocus,index){
27390         return;
27391         
27392     },
27393     select: function () {
27394         //Roo.log("SELECT CALLED");
27395     },
27396      
27397     selectByValue : function(xv, scrollIntoView){
27398         var ar = this.getValueArray();
27399         var sels = [];
27400         
27401         Roo.each(ar, function(v) {
27402             if(v === undefined || v === null){
27403                 return;
27404             }
27405             var r = this.findRecord(this.valueField, v);
27406             if(r){
27407                 sels.push(this.store.indexOf(r))
27408                 
27409             }
27410         },this);
27411         this.view.select(sels);
27412         return false;
27413     },
27414     
27415     
27416     
27417     onSelect : function(record, index){
27418        // Roo.log("onselect Called");
27419        // this is only called by the clear button now..
27420         this.view.clearSelections();
27421         this.setValue('[]');
27422         if (this.value != this.valueBefore) {
27423             this.fireEvent('change', this, this.value, this.valueBefore);
27424             this.valueBefore = this.value;
27425         }
27426     },
27427     getValueArray : function()
27428     {
27429         var ar = [] ;
27430         
27431         try {
27432             //Roo.log(this.value);
27433             if (typeof(this.value) == 'undefined') {
27434                 return [];
27435             }
27436             var ar = Roo.decode(this.value);
27437             return  ar instanceof Array ? ar : []; //?? valid?
27438             
27439         } catch(e) {
27440             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27441             return [];
27442         }
27443          
27444     },
27445     expand : function ()
27446     {
27447         
27448         Roo.form.ComboCheck.superclass.expand.call(this);
27449         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27450         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27451         
27452
27453     },
27454     
27455     collapse : function(){
27456         Roo.form.ComboCheck.superclass.collapse.call(this);
27457         var sl = this.view.getSelectedIndexes();
27458         var st = this.store;
27459         var nv = [];
27460         var tv = [];
27461         var r;
27462         Roo.each(sl, function(i) {
27463             r = st.getAt(i);
27464             nv.push(r.get(this.valueField));
27465         },this);
27466         this.setValue(Roo.encode(nv));
27467         if (this.value != this.valueBefore) {
27468
27469             this.fireEvent('change', this, this.value, this.valueBefore);
27470             this.valueBefore = this.value;
27471         }
27472         
27473     },
27474     
27475     setValue : function(v){
27476         // Roo.log(v);
27477         this.value = v;
27478         
27479         var vals = this.getValueArray();
27480         var tv = [];
27481         Roo.each(vals, function(k) {
27482             var r = this.findRecord(this.valueField, k);
27483             if(r){
27484                 tv.push(r.data[this.displayField]);
27485             }else if(this.valueNotFoundText !== undefined){
27486                 tv.push( this.valueNotFoundText );
27487             }
27488         },this);
27489        // Roo.log(tv);
27490         
27491         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27492         this.hiddenField.value = v;
27493         this.value = v;
27494     }
27495     
27496 });/*
27497  * Based on:
27498  * Ext JS Library 1.1.1
27499  * Copyright(c) 2006-2007, Ext JS, LLC.
27500  *
27501  * Originally Released Under LGPL - original licence link has changed is not relivant.
27502  *
27503  * Fork - LGPL
27504  * <script type="text/javascript">
27505  */
27506  
27507 /**
27508  * @class Roo.form.Signature
27509  * @extends Roo.form.Field
27510  * Signature field.  
27511  * @constructor
27512  * 
27513  * @param {Object} config Configuration options
27514  */
27515
27516 Roo.form.Signature = function(config){
27517     Roo.form.Signature.superclass.constructor.call(this, config);
27518     
27519     this.addEvents({// not in used??
27520          /**
27521          * @event confirm
27522          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27523              * @param {Roo.form.Signature} combo This combo box
27524              */
27525         'confirm' : true,
27526         /**
27527          * @event reset
27528          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27529              * @param {Roo.form.ComboBox} combo This combo box
27530              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27531              */
27532         'reset' : true
27533     });
27534 };
27535
27536 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27537     /**
27538      * @cfg {Object} labels Label to use when rendering a form.
27539      * defaults to 
27540      * labels : { 
27541      *      clear : "Clear",
27542      *      confirm : "Confirm"
27543      *  }
27544      */
27545     labels : { 
27546         clear : "Clear",
27547         confirm : "Confirm"
27548     },
27549     /**
27550      * @cfg {Number} width The signature panel width (defaults to 300)
27551      */
27552     width: 300,
27553     /**
27554      * @cfg {Number} height The signature panel height (defaults to 100)
27555      */
27556     height : 100,
27557     /**
27558      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27559      */
27560     allowBlank : false,
27561     
27562     //private
27563     // {Object} signPanel The signature SVG panel element (defaults to {})
27564     signPanel : {},
27565     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27566     isMouseDown : false,
27567     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27568     isConfirmed : false,
27569     // {String} signatureTmp SVG mapping string (defaults to empty string)
27570     signatureTmp : '',
27571     
27572     
27573     defaultAutoCreate : { // modified by initCompnoent..
27574         tag: "input",
27575         type:"hidden"
27576     },
27577
27578     // private
27579     onRender : function(ct, position){
27580         
27581         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27582         
27583         this.wrap = this.el.wrap({
27584             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27585         });
27586         
27587         this.createToolbar(this);
27588         this.signPanel = this.wrap.createChild({
27589                 tag: 'div',
27590                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27591             }, this.el
27592         );
27593             
27594         this.svgID = Roo.id();
27595         this.svgEl = this.signPanel.createChild({
27596               xmlns : 'http://www.w3.org/2000/svg',
27597               tag : 'svg',
27598               id : this.svgID + "-svg",
27599               width: this.width,
27600               height: this.height,
27601               viewBox: '0 0 '+this.width+' '+this.height,
27602               cn : [
27603                 {
27604                     tag: "rect",
27605                     id: this.svgID + "-svg-r",
27606                     width: this.width,
27607                     height: this.height,
27608                     fill: "#ffa"
27609                 },
27610                 {
27611                     tag: "line",
27612                     id: this.svgID + "-svg-l",
27613                     x1: "0", // start
27614                     y1: (this.height*0.8), // start set the line in 80% of height
27615                     x2: this.width, // end
27616                     y2: (this.height*0.8), // end set the line in 80% of height
27617                     'stroke': "#666",
27618                     'stroke-width': "1",
27619                     'stroke-dasharray': "3",
27620                     'shape-rendering': "crispEdges",
27621                     'pointer-events': "none"
27622                 },
27623                 {
27624                     tag: "path",
27625                     id: this.svgID + "-svg-p",
27626                     'stroke': "navy",
27627                     'stroke-width': "3",
27628                     'fill': "none",
27629                     'pointer-events': 'none'
27630                 }
27631               ]
27632         });
27633         this.createSVG();
27634         this.svgBox = this.svgEl.dom.getScreenCTM();
27635     },
27636     createSVG : function(){ 
27637         var svg = this.signPanel;
27638         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27639         var t = this;
27640
27641         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27642         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27643         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27644         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27645         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27646         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27647         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27648         
27649     },
27650     isTouchEvent : function(e){
27651         return e.type.match(/^touch/);
27652     },
27653     getCoords : function (e) {
27654         var pt    = this.svgEl.dom.createSVGPoint();
27655         pt.x = e.clientX; 
27656         pt.y = e.clientY;
27657         if (this.isTouchEvent(e)) {
27658             pt.x =  e.targetTouches[0].clientX;
27659             pt.y = e.targetTouches[0].clientY;
27660         }
27661         var a = this.svgEl.dom.getScreenCTM();
27662         var b = a.inverse();
27663         var mx = pt.matrixTransform(b);
27664         return mx.x + ',' + mx.y;
27665     },
27666     //mouse event headler 
27667     down : function (e) {
27668         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27669         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27670         
27671         this.isMouseDown = true;
27672         
27673         e.preventDefault();
27674     },
27675     move : function (e) {
27676         if (this.isMouseDown) {
27677             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27678             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27679         }
27680         
27681         e.preventDefault();
27682     },
27683     up : function (e) {
27684         this.isMouseDown = false;
27685         var sp = this.signatureTmp.split(' ');
27686         
27687         if(sp.length > 1){
27688             if(!sp[sp.length-2].match(/^L/)){
27689                 sp.pop();
27690                 sp.pop();
27691                 sp.push("");
27692                 this.signatureTmp = sp.join(" ");
27693             }
27694         }
27695         if(this.getValue() != this.signatureTmp){
27696             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27697             this.isConfirmed = false;
27698         }
27699         e.preventDefault();
27700     },
27701     
27702     /**
27703      * Protected method that will not generally be called directly. It
27704      * is called when the editor creates its toolbar. Override this method if you need to
27705      * add custom toolbar buttons.
27706      * @param {HtmlEditor} editor
27707      */
27708     createToolbar : function(editor){
27709          function btn(id, toggle, handler){
27710             var xid = fid + '-'+ id ;
27711             return {
27712                 id : xid,
27713                 cmd : id,
27714                 cls : 'x-btn-icon x-edit-'+id,
27715                 enableToggle:toggle !== false,
27716                 scope: editor, // was editor...
27717                 handler:handler||editor.relayBtnCmd,
27718                 clickEvent:'mousedown',
27719                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27720                 tabIndex:-1
27721             };
27722         }
27723         
27724         
27725         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27726         this.tb = tb;
27727         this.tb.add(
27728            {
27729                 cls : ' x-signature-btn x-signature-'+id,
27730                 scope: editor, // was editor...
27731                 handler: this.reset,
27732                 clickEvent:'mousedown',
27733                 text: this.labels.clear
27734             },
27735             {
27736                  xtype : 'Fill',
27737                  xns: Roo.Toolbar
27738             }, 
27739             {
27740                 cls : '  x-signature-btn x-signature-'+id,
27741                 scope: editor, // was editor...
27742                 handler: this.confirmHandler,
27743                 clickEvent:'mousedown',
27744                 text: this.labels.confirm
27745             }
27746         );
27747     
27748     },
27749     //public
27750     /**
27751      * when user is clicked confirm then show this image.....
27752      * 
27753      * @return {String} Image Data URI
27754      */
27755     getImageDataURI : function(){
27756         var svg = this.svgEl.dom.parentNode.innerHTML;
27757         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27758         return src; 
27759     },
27760     /**
27761      * 
27762      * @return {Boolean} this.isConfirmed
27763      */
27764     getConfirmed : function(){
27765         return this.isConfirmed;
27766     },
27767     /**
27768      * 
27769      * @return {Number} this.width
27770      */
27771     getWidth : function(){
27772         return this.width;
27773     },
27774     /**
27775      * 
27776      * @return {Number} this.height
27777      */
27778     getHeight : function(){
27779         return this.height;
27780     },
27781     // private
27782     getSignature : function(){
27783         return this.signatureTmp;
27784     },
27785     // private
27786     reset : function(){
27787         this.signatureTmp = '';
27788         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27789         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27790         this.isConfirmed = false;
27791         Roo.form.Signature.superclass.reset.call(this);
27792     },
27793     setSignature : function(s){
27794         this.signatureTmp = s;
27795         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27796         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27797         this.setValue(s);
27798         this.isConfirmed = false;
27799         Roo.form.Signature.superclass.reset.call(this);
27800     }, 
27801     test : function(){
27802 //        Roo.log(this.signPanel.dom.contentWindow.up())
27803     },
27804     //private
27805     setConfirmed : function(){
27806         
27807         
27808         
27809 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27810     },
27811     // private
27812     confirmHandler : function(){
27813         if(!this.getSignature()){
27814             return;
27815         }
27816         
27817         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27818         this.setValue(this.getSignature());
27819         this.isConfirmed = true;
27820         
27821         this.fireEvent('confirm', this);
27822     },
27823     // private
27824     // Subclasses should provide the validation implementation by overriding this
27825     validateValue : function(value){
27826         if(this.allowBlank){
27827             return true;
27828         }
27829         
27830         if(this.isConfirmed){
27831             return true;
27832         }
27833         return false;
27834     }
27835 });/*
27836  * Based on:
27837  * Ext JS Library 1.1.1
27838  * Copyright(c) 2006-2007, Ext JS, LLC.
27839  *
27840  * Originally Released Under LGPL - original licence link has changed is not relivant.
27841  *
27842  * Fork - LGPL
27843  * <script type="text/javascript">
27844  */
27845  
27846
27847 /**
27848  * @class Roo.form.ComboBox
27849  * @extends Roo.form.TriggerField
27850  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27851  * @constructor
27852  * Create a new ComboBox.
27853  * @param {Object} config Configuration options
27854  */
27855 Roo.form.Select = function(config){
27856     Roo.form.Select.superclass.constructor.call(this, config);
27857      
27858 };
27859
27860 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27861     /**
27862      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27863      */
27864     /**
27865      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27866      * rendering into an Roo.Editor, defaults to false)
27867      */
27868     /**
27869      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27870      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27871      */
27872     /**
27873      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27874      */
27875     /**
27876      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27877      * the dropdown list (defaults to undefined, with no header element)
27878      */
27879
27880      /**
27881      * @cfg {String/Roo.Template} tpl The template to use to render the output
27882      */
27883      
27884     // private
27885     defaultAutoCreate : {tag: "select"  },
27886     /**
27887      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27888      */
27889     listWidth: undefined,
27890     /**
27891      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27892      * mode = 'remote' or 'text' if mode = 'local')
27893      */
27894     displayField: undefined,
27895     /**
27896      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27897      * mode = 'remote' or 'value' if mode = 'local'). 
27898      * Note: use of a valueField requires the user make a selection
27899      * in order for a value to be mapped.
27900      */
27901     valueField: undefined,
27902     
27903     
27904     /**
27905      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27906      * field's data value (defaults to the underlying DOM element's name)
27907      */
27908     hiddenName: undefined,
27909     /**
27910      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27911      */
27912     listClass: '',
27913     /**
27914      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27915      */
27916     selectedClass: 'x-combo-selected',
27917     /**
27918      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27919      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27920      * which displays a downward arrow icon).
27921      */
27922     triggerClass : 'x-form-arrow-trigger',
27923     /**
27924      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27925      */
27926     shadow:'sides',
27927     /**
27928      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27929      * anchor positions (defaults to 'tl-bl')
27930      */
27931     listAlign: 'tl-bl?',
27932     /**
27933      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27934      */
27935     maxHeight: 300,
27936     /**
27937      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27938      * query specified by the allQuery config option (defaults to 'query')
27939      */
27940     triggerAction: 'query',
27941     /**
27942      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27943      * (defaults to 4, does not apply if editable = false)
27944      */
27945     minChars : 4,
27946     /**
27947      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27948      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27949      */
27950     typeAhead: false,
27951     /**
27952      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27953      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27954      */
27955     queryDelay: 500,
27956     /**
27957      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27958      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27959      */
27960     pageSize: 0,
27961     /**
27962      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27963      * when editable = true (defaults to false)
27964      */
27965     selectOnFocus:false,
27966     /**
27967      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27968      */
27969     queryParam: 'query',
27970     /**
27971      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27972      * when mode = 'remote' (defaults to 'Loading...')
27973      */
27974     loadingText: 'Loading...',
27975     /**
27976      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27977      */
27978     resizable: false,
27979     /**
27980      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27981      */
27982     handleHeight : 8,
27983     /**
27984      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27985      * traditional select (defaults to true)
27986      */
27987     editable: true,
27988     /**
27989      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27990      */
27991     allQuery: '',
27992     /**
27993      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27994      */
27995     mode: 'remote',
27996     /**
27997      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27998      * listWidth has a higher value)
27999      */
28000     minListWidth : 70,
28001     /**
28002      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28003      * allow the user to set arbitrary text into the field (defaults to false)
28004      */
28005     forceSelection:false,
28006     /**
28007      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28008      * if typeAhead = true (defaults to 250)
28009      */
28010     typeAheadDelay : 250,
28011     /**
28012      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28013      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28014      */
28015     valueNotFoundText : undefined,
28016     
28017     /**
28018      * @cfg {String} defaultValue The value displayed after loading the store.
28019      */
28020     defaultValue: '',
28021     
28022     /**
28023      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28024      */
28025     blockFocus : false,
28026     
28027     /**
28028      * @cfg {Boolean} disableClear Disable showing of clear button.
28029      */
28030     disableClear : false,
28031     /**
28032      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28033      */
28034     alwaysQuery : false,
28035     
28036     //private
28037     addicon : false,
28038     editicon: false,
28039     
28040     // element that contains real text value.. (when hidden is used..)
28041      
28042     // private
28043     onRender : function(ct, position){
28044         Roo.form.Field.prototype.onRender.call(this, ct, position);
28045         
28046         if(this.store){
28047             this.store.on('beforeload', this.onBeforeLoad, this);
28048             this.store.on('load', this.onLoad, this);
28049             this.store.on('loadexception', this.onLoadException, this);
28050             this.store.load({});
28051         }
28052         
28053         
28054         
28055     },
28056
28057     // private
28058     initEvents : function(){
28059         //Roo.form.ComboBox.superclass.initEvents.call(this);
28060  
28061     },
28062
28063     onDestroy : function(){
28064        
28065         if(this.store){
28066             this.store.un('beforeload', this.onBeforeLoad, this);
28067             this.store.un('load', this.onLoad, this);
28068             this.store.un('loadexception', this.onLoadException, this);
28069         }
28070         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28071     },
28072
28073     // private
28074     fireKey : function(e){
28075         if(e.isNavKeyPress() && !this.list.isVisible()){
28076             this.fireEvent("specialkey", this, e);
28077         }
28078     },
28079
28080     // private
28081     onResize: function(w, h){
28082         
28083         return; 
28084     
28085         
28086     },
28087
28088     /**
28089      * Allow or prevent the user from directly editing the field text.  If false is passed,
28090      * the user will only be able to select from the items defined in the dropdown list.  This method
28091      * is the runtime equivalent of setting the 'editable' config option at config time.
28092      * @param {Boolean} value True to allow the user to directly edit the field text
28093      */
28094     setEditable : function(value){
28095          
28096     },
28097
28098     // private
28099     onBeforeLoad : function(){
28100         
28101         Roo.log("Select before load");
28102         return;
28103     
28104         this.innerList.update(this.loadingText ?
28105                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28106         //this.restrictHeight();
28107         this.selectedIndex = -1;
28108     },
28109
28110     // private
28111     onLoad : function(){
28112
28113     
28114         var dom = this.el.dom;
28115         dom.innerHTML = '';
28116          var od = dom.ownerDocument;
28117          
28118         if (this.emptyText) {
28119             var op = od.createElement('option');
28120             op.setAttribute('value', '');
28121             op.innerHTML = String.format('{0}', this.emptyText);
28122             dom.appendChild(op);
28123         }
28124         if(this.store.getCount() > 0){
28125            
28126             var vf = this.valueField;
28127             var df = this.displayField;
28128             this.store.data.each(function(r) {
28129                 // which colmsn to use... testing - cdoe / title..
28130                 var op = od.createElement('option');
28131                 op.setAttribute('value', r.data[vf]);
28132                 op.innerHTML = String.format('{0}', r.data[df]);
28133                 dom.appendChild(op);
28134             });
28135             if (typeof(this.defaultValue != 'undefined')) {
28136                 this.setValue(this.defaultValue);
28137             }
28138             
28139              
28140         }else{
28141             //this.onEmptyResults();
28142         }
28143         //this.el.focus();
28144     },
28145     // private
28146     onLoadException : function()
28147     {
28148         dom.innerHTML = '';
28149             
28150         Roo.log("Select on load exception");
28151         return;
28152     
28153         this.collapse();
28154         Roo.log(this.store.reader.jsonData);
28155         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28156             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28157         }
28158         
28159         
28160     },
28161     // private
28162     onTypeAhead : function(){
28163          
28164     },
28165
28166     // private
28167     onSelect : function(record, index){
28168         Roo.log('on select?');
28169         return;
28170         if(this.fireEvent('beforeselect', this, record, index) !== false){
28171             this.setFromData(index > -1 ? record.data : false);
28172             this.collapse();
28173             this.fireEvent('select', this, record, index);
28174         }
28175     },
28176
28177     /**
28178      * Returns the currently selected field value or empty string if no value is set.
28179      * @return {String} value The selected value
28180      */
28181     getValue : function(){
28182         var dom = this.el.dom;
28183         this.value = dom.options[dom.selectedIndex].value;
28184         return this.value;
28185         
28186     },
28187
28188     /**
28189      * Clears any text/value currently set in the field
28190      */
28191     clearValue : function(){
28192         this.value = '';
28193         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28194         
28195     },
28196
28197     /**
28198      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28199      * will be displayed in the field.  If the value does not match the data value of an existing item,
28200      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28201      * Otherwise the field will be blank (although the value will still be set).
28202      * @param {String} value The value to match
28203      */
28204     setValue : function(v){
28205         var d = this.el.dom;
28206         for (var i =0; i < d.options.length;i++) {
28207             if (v == d.options[i].value) {
28208                 d.selectedIndex = i;
28209                 this.value = v;
28210                 return;
28211             }
28212         }
28213         this.clearValue();
28214     },
28215     /**
28216      * @property {Object} the last set data for the element
28217      */
28218     
28219     lastData : false,
28220     /**
28221      * Sets the value of the field based on a object which is related to the record format for the store.
28222      * @param {Object} value the value to set as. or false on reset?
28223      */
28224     setFromData : function(o){
28225         Roo.log('setfrom data?');
28226          
28227         
28228         
28229     },
28230     // private
28231     reset : function(){
28232         this.clearValue();
28233     },
28234     // private
28235     findRecord : function(prop, value){
28236         
28237         return false;
28238     
28239         var record;
28240         if(this.store.getCount() > 0){
28241             this.store.each(function(r){
28242                 if(r.data[prop] == value){
28243                     record = r;
28244                     return false;
28245                 }
28246                 return true;
28247             });
28248         }
28249         return record;
28250     },
28251     
28252     getName: function()
28253     {
28254         // returns hidden if it's set..
28255         if (!this.rendered) {return ''};
28256         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28257         
28258     },
28259      
28260
28261     
28262
28263     // private
28264     onEmptyResults : function(){
28265         Roo.log('empty results');
28266         //this.collapse();
28267     },
28268
28269     /**
28270      * Returns true if the dropdown list is expanded, else false.
28271      */
28272     isExpanded : function(){
28273         return false;
28274     },
28275
28276     /**
28277      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28278      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28279      * @param {String} value The data value of the item to select
28280      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28281      * selected item if it is not currently in view (defaults to true)
28282      * @return {Boolean} True if the value matched an item in the list, else false
28283      */
28284     selectByValue : function(v, scrollIntoView){
28285         Roo.log('select By Value');
28286         return false;
28287     
28288         if(v !== undefined && v !== null){
28289             var r = this.findRecord(this.valueField || this.displayField, v);
28290             if(r){
28291                 this.select(this.store.indexOf(r), scrollIntoView);
28292                 return true;
28293             }
28294         }
28295         return false;
28296     },
28297
28298     /**
28299      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28300      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28301      * @param {Number} index The zero-based index of the list item to select
28302      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28303      * selected item if it is not currently in view (defaults to true)
28304      */
28305     select : function(index, scrollIntoView){
28306         Roo.log('select ');
28307         return  ;
28308         
28309         this.selectedIndex = index;
28310         this.view.select(index);
28311         if(scrollIntoView !== false){
28312             var el = this.view.getNode(index);
28313             if(el){
28314                 this.innerList.scrollChildIntoView(el, false);
28315             }
28316         }
28317     },
28318
28319       
28320
28321     // private
28322     validateBlur : function(){
28323         
28324         return;
28325         
28326     },
28327
28328     // private
28329     initQuery : function(){
28330         this.doQuery(this.getRawValue());
28331     },
28332
28333     // private
28334     doForce : function(){
28335         if(this.el.dom.value.length > 0){
28336             this.el.dom.value =
28337                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28338              
28339         }
28340     },
28341
28342     /**
28343      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28344      * query allowing the query action to be canceled if needed.
28345      * @param {String} query The SQL query to execute
28346      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28347      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28348      * saved in the current store (defaults to false)
28349      */
28350     doQuery : function(q, forceAll){
28351         
28352         Roo.log('doQuery?');
28353         if(q === undefined || q === null){
28354             q = '';
28355         }
28356         var qe = {
28357             query: q,
28358             forceAll: forceAll,
28359             combo: this,
28360             cancel:false
28361         };
28362         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28363             return false;
28364         }
28365         q = qe.query;
28366         forceAll = qe.forceAll;
28367         if(forceAll === true || (q.length >= this.minChars)){
28368             if(this.lastQuery != q || this.alwaysQuery){
28369                 this.lastQuery = q;
28370                 if(this.mode == 'local'){
28371                     this.selectedIndex = -1;
28372                     if(forceAll){
28373                         this.store.clearFilter();
28374                     }else{
28375                         this.store.filter(this.displayField, q);
28376                     }
28377                     this.onLoad();
28378                 }else{
28379                     this.store.baseParams[this.queryParam] = q;
28380                     this.store.load({
28381                         params: this.getParams(q)
28382                     });
28383                     this.expand();
28384                 }
28385             }else{
28386                 this.selectedIndex = -1;
28387                 this.onLoad();   
28388             }
28389         }
28390     },
28391
28392     // private
28393     getParams : function(q){
28394         var p = {};
28395         //p[this.queryParam] = q;
28396         if(this.pageSize){
28397             p.start = 0;
28398             p.limit = this.pageSize;
28399         }
28400         return p;
28401     },
28402
28403     /**
28404      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28405      */
28406     collapse : function(){
28407         
28408     },
28409
28410     // private
28411     collapseIf : function(e){
28412         
28413     },
28414
28415     /**
28416      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28417      */
28418     expand : function(){
28419         
28420     } ,
28421
28422     // private
28423      
28424
28425     /** 
28426     * @cfg {Boolean} grow 
28427     * @hide 
28428     */
28429     /** 
28430     * @cfg {Number} growMin 
28431     * @hide 
28432     */
28433     /** 
28434     * @cfg {Number} growMax 
28435     * @hide 
28436     */
28437     /**
28438      * @hide
28439      * @method autoSize
28440      */
28441     
28442     setWidth : function()
28443     {
28444         
28445     },
28446     getResizeEl : function(){
28447         return this.el;
28448     }
28449 });//<script type="text/javasscript">
28450  
28451
28452 /**
28453  * @class Roo.DDView
28454  * A DnD enabled version of Roo.View.
28455  * @param {Element/String} container The Element in which to create the View.
28456  * @param {String} tpl The template string used to create the markup for each element of the View
28457  * @param {Object} config The configuration properties. These include all the config options of
28458  * {@link Roo.View} plus some specific to this class.<br>
28459  * <p>
28460  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28461  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28462  * <p>
28463  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28464 .x-view-drag-insert-above {
28465         border-top:1px dotted #3366cc;
28466 }
28467 .x-view-drag-insert-below {
28468         border-bottom:1px dotted #3366cc;
28469 }
28470 </code></pre>
28471  * 
28472  */
28473  
28474 Roo.DDView = function(container, tpl, config) {
28475     Roo.DDView.superclass.constructor.apply(this, arguments);
28476     this.getEl().setStyle("outline", "0px none");
28477     this.getEl().unselectable();
28478     if (this.dragGroup) {
28479         this.setDraggable(this.dragGroup.split(","));
28480     }
28481     if (this.dropGroup) {
28482         this.setDroppable(this.dropGroup.split(","));
28483     }
28484     if (this.deletable) {
28485         this.setDeletable();
28486     }
28487     this.isDirtyFlag = false;
28488         this.addEvents({
28489                 "drop" : true
28490         });
28491 };
28492
28493 Roo.extend(Roo.DDView, Roo.View, {
28494 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28495 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28496 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28497 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28498
28499         isFormField: true,
28500
28501         reset: Roo.emptyFn,
28502         
28503         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28504
28505         validate: function() {
28506                 return true;
28507         },
28508         
28509         destroy: function() {
28510                 this.purgeListeners();
28511                 this.getEl.removeAllListeners();
28512                 this.getEl().remove();
28513                 if (this.dragZone) {
28514                         if (this.dragZone.destroy) {
28515                                 this.dragZone.destroy();
28516                         }
28517                 }
28518                 if (this.dropZone) {
28519                         if (this.dropZone.destroy) {
28520                                 this.dropZone.destroy();
28521                         }
28522                 }
28523         },
28524
28525 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28526         getName: function() {
28527                 return this.name;
28528         },
28529
28530 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28531         setValue: function(v) {
28532                 if (!this.store) {
28533                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28534                 }
28535                 var data = {};
28536                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28537                 this.store.proxy = new Roo.data.MemoryProxy(data);
28538                 this.store.load();
28539         },
28540
28541 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28542         getValue: function() {
28543                 var result = '(';
28544                 this.store.each(function(rec) {
28545                         result += rec.id + ',';
28546                 });
28547                 return result.substr(0, result.length - 1) + ')';
28548         },
28549         
28550         getIds: function() {
28551                 var i = 0, result = new Array(this.store.getCount());
28552                 this.store.each(function(rec) {
28553                         result[i++] = rec.id;
28554                 });
28555                 return result;
28556         },
28557         
28558         isDirty: function() {
28559                 return this.isDirtyFlag;
28560         },
28561
28562 /**
28563  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28564  *      whole Element becomes the target, and this causes the drop gesture to append.
28565  */
28566     getTargetFromEvent : function(e) {
28567                 var target = e.getTarget();
28568                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28569                 target = target.parentNode;
28570                 }
28571                 if (!target) {
28572                         target = this.el.dom.lastChild || this.el.dom;
28573                 }
28574                 return target;
28575     },
28576
28577 /**
28578  *      Create the drag data which consists of an object which has the property "ddel" as
28579  *      the drag proxy element. 
28580  */
28581     getDragData : function(e) {
28582         var target = this.findItemFromChild(e.getTarget());
28583                 if(target) {
28584                         this.handleSelection(e);
28585                         var selNodes = this.getSelectedNodes();
28586             var dragData = {
28587                 source: this,
28588                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28589                 nodes: selNodes,
28590                 records: []
28591                         };
28592                         var selectedIndices = this.getSelectedIndexes();
28593                         for (var i = 0; i < selectedIndices.length; i++) {
28594                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28595                         }
28596                         if (selNodes.length == 1) {
28597                                 dragData.ddel = target.cloneNode(true); // the div element
28598                         } else {
28599                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28600                                 div.className = 'multi-proxy';
28601                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28602                                         div.appendChild(selNodes[i].cloneNode(true));
28603                                 }
28604                                 dragData.ddel = div;
28605                         }
28606             //console.log(dragData)
28607             //console.log(dragData.ddel.innerHTML)
28608                         return dragData;
28609                 }
28610         //console.log('nodragData')
28611                 return false;
28612     },
28613     
28614 /**     Specify to which ddGroup items in this DDView may be dragged. */
28615     setDraggable: function(ddGroup) {
28616         if (ddGroup instanceof Array) {
28617                 Roo.each(ddGroup, this.setDraggable, this);
28618                 return;
28619         }
28620         if (this.dragZone) {
28621                 this.dragZone.addToGroup(ddGroup);
28622         } else {
28623                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28624                                 containerScroll: true,
28625                                 ddGroup: ddGroup 
28626
28627                         });
28628 //                      Draggability implies selection. DragZone's mousedown selects the element.
28629                         if (!this.multiSelect) { this.singleSelect = true; }
28630
28631 //                      Wire the DragZone's handlers up to methods in *this*
28632                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28633                 }
28634     },
28635
28636 /**     Specify from which ddGroup this DDView accepts drops. */
28637     setDroppable: function(ddGroup) {
28638         if (ddGroup instanceof Array) {
28639                 Roo.each(ddGroup, this.setDroppable, this);
28640                 return;
28641         }
28642         if (this.dropZone) {
28643                 this.dropZone.addToGroup(ddGroup);
28644         } else {
28645                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28646                                 containerScroll: true,
28647                                 ddGroup: ddGroup
28648                         });
28649
28650 //                      Wire the DropZone's handlers up to methods in *this*
28651                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28652                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28653                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28654                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28655                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28656                 }
28657     },
28658
28659 /**     Decide whether to drop above or below a View node. */
28660     getDropPoint : function(e, n, dd){
28661         if (n == this.el.dom) { return "above"; }
28662                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28663                 var c = t + (b - t) / 2;
28664                 var y = Roo.lib.Event.getPageY(e);
28665                 if(y <= c) {
28666                         return "above";
28667                 }else{
28668                         return "below";
28669                 }
28670     },
28671
28672     onNodeEnter : function(n, dd, e, data){
28673                 return false;
28674     },
28675     
28676     onNodeOver : function(n, dd, e, data){
28677                 var pt = this.getDropPoint(e, n, dd);
28678                 // set the insert point style on the target node
28679                 var dragElClass = this.dropNotAllowed;
28680                 if (pt) {
28681                         var targetElClass;
28682                         if (pt == "above"){
28683                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28684                                 targetElClass = "x-view-drag-insert-above";
28685                         } else {
28686                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28687                                 targetElClass = "x-view-drag-insert-below";
28688                         }
28689                         if (this.lastInsertClass != targetElClass){
28690                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28691                                 this.lastInsertClass = targetElClass;
28692                         }
28693                 }
28694                 return dragElClass;
28695         },
28696
28697     onNodeOut : function(n, dd, e, data){
28698                 this.removeDropIndicators(n);
28699     },
28700
28701     onNodeDrop : function(n, dd, e, data){
28702         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28703                 return false;
28704         }
28705         var pt = this.getDropPoint(e, n, dd);
28706                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28707                 if (pt == "below") { insertAt++; }
28708                 for (var i = 0; i < data.records.length; i++) {
28709                         var r = data.records[i];
28710                         var dup = this.store.getById(r.id);
28711                         if (dup && (dd != this.dragZone)) {
28712                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28713                         } else {
28714                                 if (data.copy) {
28715                                         this.store.insert(insertAt++, r.copy());
28716                                 } else {
28717                                         data.source.isDirtyFlag = true;
28718                                         r.store.remove(r);
28719                                         this.store.insert(insertAt++, r);
28720                                 }
28721                                 this.isDirtyFlag = true;
28722                         }
28723                 }
28724                 this.dragZone.cachedTarget = null;
28725                 return true;
28726     },
28727
28728     removeDropIndicators : function(n){
28729                 if(n){
28730                         Roo.fly(n).removeClass([
28731                                 "x-view-drag-insert-above",
28732                                 "x-view-drag-insert-below"]);
28733                         this.lastInsertClass = "_noclass";
28734                 }
28735     },
28736
28737 /**
28738  *      Utility method. Add a delete option to the DDView's context menu.
28739  *      @param {String} imageUrl The URL of the "delete" icon image.
28740  */
28741         setDeletable: function(imageUrl) {
28742                 if (!this.singleSelect && !this.multiSelect) {
28743                         this.singleSelect = true;
28744                 }
28745                 var c = this.getContextMenu();
28746                 this.contextMenu.on("itemclick", function(item) {
28747                         switch (item.id) {
28748                                 case "delete":
28749                                         this.remove(this.getSelectedIndexes());
28750                                         break;
28751                         }
28752                 }, this);
28753                 this.contextMenu.add({
28754                         icon: imageUrl,
28755                         id: "delete",
28756                         text: 'Delete'
28757                 });
28758         },
28759         
28760 /**     Return the context menu for this DDView. */
28761         getContextMenu: function() {
28762                 if (!this.contextMenu) {
28763 //                      Create the View's context menu
28764                         this.contextMenu = new Roo.menu.Menu({
28765                                 id: this.id + "-contextmenu"
28766                         });
28767                         this.el.on("contextmenu", this.showContextMenu, this);
28768                 }
28769                 return this.contextMenu;
28770         },
28771         
28772         disableContextMenu: function() {
28773                 if (this.contextMenu) {
28774                         this.el.un("contextmenu", this.showContextMenu, this);
28775                 }
28776         },
28777
28778         showContextMenu: function(e, item) {
28779         item = this.findItemFromChild(e.getTarget());
28780                 if (item) {
28781                         e.stopEvent();
28782                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28783                         this.contextMenu.showAt(e.getXY());
28784             }
28785     },
28786
28787 /**
28788  *      Remove {@link Roo.data.Record}s at the specified indices.
28789  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28790  */
28791     remove: function(selectedIndices) {
28792                 selectedIndices = [].concat(selectedIndices);
28793                 for (var i = 0; i < selectedIndices.length; i++) {
28794                         var rec = this.store.getAt(selectedIndices[i]);
28795                         this.store.remove(rec);
28796                 }
28797     },
28798
28799 /**
28800  *      Double click fires the event, but also, if this is draggable, and there is only one other
28801  *      related DropZone, it transfers the selected node.
28802  */
28803     onDblClick : function(e){
28804         var item = this.findItemFromChild(e.getTarget());
28805         if(item){
28806             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28807                 return false;
28808             }
28809             if (this.dragGroup) {
28810                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28811                     while (targets.indexOf(this.dropZone) > -1) {
28812                             targets.remove(this.dropZone);
28813                                 }
28814                     if (targets.length == 1) {
28815                                         this.dragZone.cachedTarget = null;
28816                         var el = Roo.get(targets[0].getEl());
28817                         var box = el.getBox(true);
28818                         targets[0].onNodeDrop(el.dom, {
28819                                 target: el.dom,
28820                                 xy: [box.x, box.y + box.height - 1]
28821                         }, null, this.getDragData(e));
28822                     }
28823                 }
28824         }
28825     },
28826     
28827     handleSelection: function(e) {
28828                 this.dragZone.cachedTarget = null;
28829         var item = this.findItemFromChild(e.getTarget());
28830         if (!item) {
28831                 this.clearSelections(true);
28832                 return;
28833         }
28834                 if (item && (this.multiSelect || this.singleSelect)){
28835                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28836                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28837                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28838                                 this.unselect(item);
28839                         } else {
28840                                 this.select(item, this.multiSelect && e.ctrlKey);
28841                                 this.lastSelection = item;
28842                         }
28843                 }
28844     },
28845
28846     onItemClick : function(item, index, e){
28847                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28848                         return false;
28849                 }
28850                 return true;
28851     },
28852
28853     unselect : function(nodeInfo, suppressEvent){
28854                 var node = this.getNode(nodeInfo);
28855                 if(node && this.isSelected(node)){
28856                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28857                                 Roo.fly(node).removeClass(this.selectedClass);
28858                                 this.selections.remove(node);
28859                                 if(!suppressEvent){
28860                                         this.fireEvent("selectionchange", this, this.selections);
28861                                 }
28862                         }
28863                 }
28864     }
28865 });
28866 /*
28867  * Based on:
28868  * Ext JS Library 1.1.1
28869  * Copyright(c) 2006-2007, Ext JS, LLC.
28870  *
28871  * Originally Released Under LGPL - original licence link has changed is not relivant.
28872  *
28873  * Fork - LGPL
28874  * <script type="text/javascript">
28875  */
28876  
28877 /**
28878  * @class Roo.LayoutManager
28879  * @extends Roo.util.Observable
28880  * Base class for layout managers.
28881  */
28882 Roo.LayoutManager = function(container, config){
28883     Roo.LayoutManager.superclass.constructor.call(this);
28884     this.el = Roo.get(container);
28885     // ie scrollbar fix
28886     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28887         document.body.scroll = "no";
28888     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28889         this.el.position('relative');
28890     }
28891     this.id = this.el.id;
28892     this.el.addClass("x-layout-container");
28893     /** false to disable window resize monitoring @type Boolean */
28894     this.monitorWindowResize = true;
28895     this.regions = {};
28896     this.addEvents({
28897         /**
28898          * @event layout
28899          * Fires when a layout is performed. 
28900          * @param {Roo.LayoutManager} this
28901          */
28902         "layout" : true,
28903         /**
28904          * @event regionresized
28905          * Fires when the user resizes a region. 
28906          * @param {Roo.LayoutRegion} region The resized region
28907          * @param {Number} newSize The new size (width for east/west, height for north/south)
28908          */
28909         "regionresized" : true,
28910         /**
28911          * @event regioncollapsed
28912          * Fires when a region is collapsed. 
28913          * @param {Roo.LayoutRegion} region The collapsed region
28914          */
28915         "regioncollapsed" : true,
28916         /**
28917          * @event regionexpanded
28918          * Fires when a region is expanded.  
28919          * @param {Roo.LayoutRegion} region The expanded region
28920          */
28921         "regionexpanded" : true
28922     });
28923     this.updating = false;
28924     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28925 };
28926
28927 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28928     /**
28929      * Returns true if this layout is currently being updated
28930      * @return {Boolean}
28931      */
28932     isUpdating : function(){
28933         return this.updating; 
28934     },
28935     
28936     /**
28937      * Suspend the LayoutManager from doing auto-layouts while
28938      * making multiple add or remove calls
28939      */
28940     beginUpdate : function(){
28941         this.updating = true;    
28942     },
28943     
28944     /**
28945      * Restore auto-layouts and optionally disable the manager from performing a layout
28946      * @param {Boolean} noLayout true to disable a layout update 
28947      */
28948     endUpdate : function(noLayout){
28949         this.updating = false;
28950         if(!noLayout){
28951             this.layout();
28952         }    
28953     },
28954     
28955     layout: function(){
28956         
28957     },
28958     
28959     onRegionResized : function(region, newSize){
28960         this.fireEvent("regionresized", region, newSize);
28961         this.layout();
28962     },
28963     
28964     onRegionCollapsed : function(region){
28965         this.fireEvent("regioncollapsed", region);
28966     },
28967     
28968     onRegionExpanded : function(region){
28969         this.fireEvent("regionexpanded", region);
28970     },
28971         
28972     /**
28973      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28974      * performs box-model adjustments.
28975      * @return {Object} The size as an object {width: (the width), height: (the height)}
28976      */
28977     getViewSize : function(){
28978         var size;
28979         if(this.el.dom != document.body){
28980             size = this.el.getSize();
28981         }else{
28982             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28983         }
28984         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28985         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28986         return size;
28987     },
28988     
28989     /**
28990      * Returns the Element this layout is bound to.
28991      * @return {Roo.Element}
28992      */
28993     getEl : function(){
28994         return this.el;
28995     },
28996     
28997     /**
28998      * Returns the specified region.
28999      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29000      * @return {Roo.LayoutRegion}
29001      */
29002     getRegion : function(target){
29003         return this.regions[target.toLowerCase()];
29004     },
29005     
29006     onWindowResize : function(){
29007         if(this.monitorWindowResize){
29008             this.layout();
29009         }
29010     }
29011 });/*
29012  * Based on:
29013  * Ext JS Library 1.1.1
29014  * Copyright(c) 2006-2007, Ext JS, LLC.
29015  *
29016  * Originally Released Under LGPL - original licence link has changed is not relivant.
29017  *
29018  * Fork - LGPL
29019  * <script type="text/javascript">
29020  */
29021 /**
29022  * @class Roo.BorderLayout
29023  * @extends Roo.LayoutManager
29024  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29025  * please see: <br><br>
29026  * <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>
29027  * <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>
29028  * Example:
29029  <pre><code>
29030  var layout = new Roo.BorderLayout(document.body, {
29031     north: {
29032         initialSize: 25,
29033         titlebar: false
29034     },
29035     west: {
29036         split:true,
29037         initialSize: 200,
29038         minSize: 175,
29039         maxSize: 400,
29040         titlebar: true,
29041         collapsible: true
29042     },
29043     east: {
29044         split:true,
29045         initialSize: 202,
29046         minSize: 175,
29047         maxSize: 400,
29048         titlebar: true,
29049         collapsible: true
29050     },
29051     south: {
29052         split:true,
29053         initialSize: 100,
29054         minSize: 100,
29055         maxSize: 200,
29056         titlebar: true,
29057         collapsible: true
29058     },
29059     center: {
29060         titlebar: true,
29061         autoScroll:true,
29062         resizeTabs: true,
29063         minTabWidth: 50,
29064         preferredTabWidth: 150
29065     }
29066 });
29067
29068 // shorthand
29069 var CP = Roo.ContentPanel;
29070
29071 layout.beginUpdate();
29072 layout.add("north", new CP("north", "North"));
29073 layout.add("south", new CP("south", {title: "South", closable: true}));
29074 layout.add("west", new CP("west", {title: "West"}));
29075 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29076 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29077 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29078 layout.getRegion("center").showPanel("center1");
29079 layout.endUpdate();
29080 </code></pre>
29081
29082 <b>The container the layout is rendered into can be either the body element or any other element.
29083 If it is not the body element, the container needs to either be an absolute positioned element,
29084 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29085 the container size if it is not the body element.</b>
29086
29087 * @constructor
29088 * Create a new BorderLayout
29089 * @param {String/HTMLElement/Element} container The container this layout is bound to
29090 * @param {Object} config Configuration options
29091  */
29092 Roo.BorderLayout = function(container, config){
29093     config = config || {};
29094     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29095     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29096     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29097         var target = this.factory.validRegions[i];
29098         if(config[target]){
29099             this.addRegion(target, config[target]);
29100         }
29101     }
29102 };
29103
29104 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29105     /**
29106      * Creates and adds a new region if it doesn't already exist.
29107      * @param {String} target The target region key (north, south, east, west or center).
29108      * @param {Object} config The regions config object
29109      * @return {BorderLayoutRegion} The new region
29110      */
29111     addRegion : function(target, config){
29112         if(!this.regions[target]){
29113             var r = this.factory.create(target, this, config);
29114             this.bindRegion(target, r);
29115         }
29116         return this.regions[target];
29117     },
29118
29119     // private (kinda)
29120     bindRegion : function(name, r){
29121         this.regions[name] = r;
29122         r.on("visibilitychange", this.layout, this);
29123         r.on("paneladded", this.layout, this);
29124         r.on("panelremoved", this.layout, this);
29125         r.on("invalidated", this.layout, this);
29126         r.on("resized", this.onRegionResized, this);
29127         r.on("collapsed", this.onRegionCollapsed, this);
29128         r.on("expanded", this.onRegionExpanded, this);
29129     },
29130
29131     /**
29132      * Performs a layout update.
29133      */
29134     layout : function(){
29135         if(this.updating) {
29136             return;
29137         }
29138         var size = this.getViewSize();
29139         var w = size.width;
29140         var h = size.height;
29141         var centerW = w;
29142         var centerH = h;
29143         var centerY = 0;
29144         var centerX = 0;
29145         //var x = 0, y = 0;
29146
29147         var rs = this.regions;
29148         var north = rs["north"];
29149         var south = rs["south"]; 
29150         var west = rs["west"];
29151         var east = rs["east"];
29152         var center = rs["center"];
29153         //if(this.hideOnLayout){ // not supported anymore
29154             //c.el.setStyle("display", "none");
29155         //}
29156         if(north && north.isVisible()){
29157             var b = north.getBox();
29158             var m = north.getMargins();
29159             b.width = w - (m.left+m.right);
29160             b.x = m.left;
29161             b.y = m.top;
29162             centerY = b.height + b.y + m.bottom;
29163             centerH -= centerY;
29164             north.updateBox(this.safeBox(b));
29165         }
29166         if(south && south.isVisible()){
29167             var b = south.getBox();
29168             var m = south.getMargins();
29169             b.width = w - (m.left+m.right);
29170             b.x = m.left;
29171             var totalHeight = (b.height + m.top + m.bottom);
29172             b.y = h - totalHeight + m.top;
29173             centerH -= totalHeight;
29174             south.updateBox(this.safeBox(b));
29175         }
29176         if(west && west.isVisible()){
29177             var b = west.getBox();
29178             var m = west.getMargins();
29179             b.height = centerH - (m.top+m.bottom);
29180             b.x = m.left;
29181             b.y = centerY + m.top;
29182             var totalWidth = (b.width + m.left + m.right);
29183             centerX += totalWidth;
29184             centerW -= totalWidth;
29185             west.updateBox(this.safeBox(b));
29186         }
29187         if(east && east.isVisible()){
29188             var b = east.getBox();
29189             var m = east.getMargins();
29190             b.height = centerH - (m.top+m.bottom);
29191             var totalWidth = (b.width + m.left + m.right);
29192             b.x = w - totalWidth + m.left;
29193             b.y = centerY + m.top;
29194             centerW -= totalWidth;
29195             east.updateBox(this.safeBox(b));
29196         }
29197         if(center){
29198             var m = center.getMargins();
29199             var centerBox = {
29200                 x: centerX + m.left,
29201                 y: centerY + m.top,
29202                 width: centerW - (m.left+m.right),
29203                 height: centerH - (m.top+m.bottom)
29204             };
29205             //if(this.hideOnLayout){
29206                 //center.el.setStyle("display", "block");
29207             //}
29208             center.updateBox(this.safeBox(centerBox));
29209         }
29210         this.el.repaint();
29211         this.fireEvent("layout", this);
29212     },
29213
29214     // private
29215     safeBox : function(box){
29216         box.width = Math.max(0, box.width);
29217         box.height = Math.max(0, box.height);
29218         return box;
29219     },
29220
29221     /**
29222      * Adds a ContentPanel (or subclass) to this layout.
29223      * @param {String} target The target region key (north, south, east, west or center).
29224      * @param {Roo.ContentPanel} panel The panel to add
29225      * @return {Roo.ContentPanel} The added panel
29226      */
29227     add : function(target, panel){
29228          
29229         target = target.toLowerCase();
29230         return this.regions[target].add(panel);
29231     },
29232
29233     /**
29234      * Remove a ContentPanel (or subclass) to this layout.
29235      * @param {String} target The target region key (north, south, east, west or center).
29236      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29237      * @return {Roo.ContentPanel} The removed panel
29238      */
29239     remove : function(target, panel){
29240         target = target.toLowerCase();
29241         return this.regions[target].remove(panel);
29242     },
29243
29244     /**
29245      * Searches all regions for a panel with the specified id
29246      * @param {String} panelId
29247      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29248      */
29249     findPanel : function(panelId){
29250         var rs = this.regions;
29251         for(var target in rs){
29252             if(typeof rs[target] != "function"){
29253                 var p = rs[target].getPanel(panelId);
29254                 if(p){
29255                     return p;
29256                 }
29257             }
29258         }
29259         return null;
29260     },
29261
29262     /**
29263      * Searches all regions for a panel with the specified id and activates (shows) it.
29264      * @param {String/ContentPanel} panelId The panels id or the panel itself
29265      * @return {Roo.ContentPanel} The shown panel or null
29266      */
29267     showPanel : function(panelId) {
29268       var rs = this.regions;
29269       for(var target in rs){
29270          var r = rs[target];
29271          if(typeof r != "function"){
29272             if(r.hasPanel(panelId)){
29273                return r.showPanel(panelId);
29274             }
29275          }
29276       }
29277       return null;
29278    },
29279
29280    /**
29281      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29282      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29283      */
29284     restoreState : function(provider){
29285         if(!provider){
29286             provider = Roo.state.Manager;
29287         }
29288         var sm = new Roo.LayoutStateManager();
29289         sm.init(this, provider);
29290     },
29291
29292     /**
29293      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29294      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29295      * a valid ContentPanel config object.  Example:
29296      * <pre><code>
29297 // Create the main layout
29298 var layout = new Roo.BorderLayout('main-ct', {
29299     west: {
29300         split:true,
29301         minSize: 175,
29302         titlebar: true
29303     },
29304     center: {
29305         title:'Components'
29306     }
29307 }, 'main-ct');
29308
29309 // Create and add multiple ContentPanels at once via configs
29310 layout.batchAdd({
29311    west: {
29312        id: 'source-files',
29313        autoCreate:true,
29314        title:'Ext Source Files',
29315        autoScroll:true,
29316        fitToFrame:true
29317    },
29318    center : {
29319        el: cview,
29320        autoScroll:true,
29321        fitToFrame:true,
29322        toolbar: tb,
29323        resizeEl:'cbody'
29324    }
29325 });
29326 </code></pre>
29327      * @param {Object} regions An object containing ContentPanel configs by region name
29328      */
29329     batchAdd : function(regions){
29330         this.beginUpdate();
29331         for(var rname in regions){
29332             var lr = this.regions[rname];
29333             if(lr){
29334                 this.addTypedPanels(lr, regions[rname]);
29335             }
29336         }
29337         this.endUpdate();
29338     },
29339
29340     // private
29341     addTypedPanels : function(lr, ps){
29342         if(typeof ps == 'string'){
29343             lr.add(new Roo.ContentPanel(ps));
29344         }
29345         else if(ps instanceof Array){
29346             for(var i =0, len = ps.length; i < len; i++){
29347                 this.addTypedPanels(lr, ps[i]);
29348             }
29349         }
29350         else if(!ps.events){ // raw config?
29351             var el = ps.el;
29352             delete ps.el; // prevent conflict
29353             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29354         }
29355         else {  // panel object assumed!
29356             lr.add(ps);
29357         }
29358     },
29359     /**
29360      * Adds a xtype elements to the layout.
29361      * <pre><code>
29362
29363 layout.addxtype({
29364        xtype : 'ContentPanel',
29365        region: 'west',
29366        items: [ .... ]
29367    }
29368 );
29369
29370 layout.addxtype({
29371         xtype : 'NestedLayoutPanel',
29372         region: 'west',
29373         layout: {
29374            center: { },
29375            west: { }   
29376         },
29377         items : [ ... list of content panels or nested layout panels.. ]
29378    }
29379 );
29380 </code></pre>
29381      * @param {Object} cfg Xtype definition of item to add.
29382      */
29383     addxtype : function(cfg)
29384     {
29385         // basically accepts a pannel...
29386         // can accept a layout region..!?!?
29387         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29388         
29389         if (!cfg.xtype.match(/Panel$/)) {
29390             return false;
29391         }
29392         var ret = false;
29393         
29394         if (typeof(cfg.region) == 'undefined') {
29395             Roo.log("Failed to add Panel, region was not set");
29396             Roo.log(cfg);
29397             return false;
29398         }
29399         var region = cfg.region;
29400         delete cfg.region;
29401         
29402           
29403         var xitems = [];
29404         if (cfg.items) {
29405             xitems = cfg.items;
29406             delete cfg.items;
29407         }
29408         var nb = false;
29409         
29410         switch(cfg.xtype) 
29411         {
29412             case 'ContentPanel':  // ContentPanel (el, cfg)
29413             case 'ScrollPanel':  // ContentPanel (el, cfg)
29414             case 'ViewPanel': 
29415                 if(cfg.autoCreate) {
29416                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29417                 } else {
29418                     var el = this.el.createChild();
29419                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29420                 }
29421                 
29422                 this.add(region, ret);
29423                 break;
29424             
29425             
29426             case 'TreePanel': // our new panel!
29427                 cfg.el = this.el.createChild();
29428                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29429                 this.add(region, ret);
29430                 break;
29431             
29432             case 'NestedLayoutPanel': 
29433                 // create a new Layout (which is  a Border Layout...
29434                 var el = this.el.createChild();
29435                 var clayout = cfg.layout;
29436                 delete cfg.layout;
29437                 clayout.items   = clayout.items  || [];
29438                 // replace this exitems with the clayout ones..
29439                 xitems = clayout.items;
29440                  
29441                 
29442                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29443                     cfg.background = false;
29444                 }
29445                 var layout = new Roo.BorderLayout(el, clayout);
29446                 
29447                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29448                 //console.log('adding nested layout panel '  + cfg.toSource());
29449                 this.add(region, ret);
29450                 nb = {}; /// find first...
29451                 break;
29452                 
29453             case 'GridPanel': 
29454             
29455                 // needs grid and region
29456                 
29457                 //var el = this.getRegion(region).el.createChild();
29458                 var el = this.el.createChild();
29459                 // create the grid first...
29460                 
29461                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29462                 delete cfg.grid;
29463                 if (region == 'center' && this.active ) {
29464                     cfg.background = false;
29465                 }
29466                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29467                 
29468                 this.add(region, ret);
29469                 if (cfg.background) {
29470                     ret.on('activate', function(gp) {
29471                         if (!gp.grid.rendered) {
29472                             gp.grid.render();
29473                         }
29474                     });
29475                 } else {
29476                     grid.render();
29477                 }
29478                 break;
29479            
29480            
29481            
29482                 
29483                 
29484                 
29485             default:
29486                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29487                     
29488                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29489                     this.add(region, ret);
29490                 } else {
29491                 
29492                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29493                     return null;
29494                 }
29495                 
29496              // GridPanel (grid, cfg)
29497             
29498         }
29499         this.beginUpdate();
29500         // add children..
29501         var region = '';
29502         var abn = {};
29503         Roo.each(xitems, function(i)  {
29504             region = nb && i.region ? i.region : false;
29505             
29506             var add = ret.addxtype(i);
29507            
29508             if (region) {
29509                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29510                 if (!i.background) {
29511                     abn[region] = nb[region] ;
29512                 }
29513             }
29514             
29515         });
29516         this.endUpdate();
29517
29518         // make the last non-background panel active..
29519         //if (nb) { Roo.log(abn); }
29520         if (nb) {
29521             
29522             for(var r in abn) {
29523                 region = this.getRegion(r);
29524                 if (region) {
29525                     // tried using nb[r], but it does not work..
29526                      
29527                     region.showPanel(abn[r]);
29528                    
29529                 }
29530             }
29531         }
29532         return ret;
29533         
29534     }
29535 });
29536
29537 /**
29538  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29539  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29540  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29541  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29542  * <pre><code>
29543 // shorthand
29544 var CP = Roo.ContentPanel;
29545
29546 var layout = Roo.BorderLayout.create({
29547     north: {
29548         initialSize: 25,
29549         titlebar: false,
29550         panels: [new CP("north", "North")]
29551     },
29552     west: {
29553         split:true,
29554         initialSize: 200,
29555         minSize: 175,
29556         maxSize: 400,
29557         titlebar: true,
29558         collapsible: true,
29559         panels: [new CP("west", {title: "West"})]
29560     },
29561     east: {
29562         split:true,
29563         initialSize: 202,
29564         minSize: 175,
29565         maxSize: 400,
29566         titlebar: true,
29567         collapsible: true,
29568         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29569     },
29570     south: {
29571         split:true,
29572         initialSize: 100,
29573         minSize: 100,
29574         maxSize: 200,
29575         titlebar: true,
29576         collapsible: true,
29577         panels: [new CP("south", {title: "South", closable: true})]
29578     },
29579     center: {
29580         titlebar: true,
29581         autoScroll:true,
29582         resizeTabs: true,
29583         minTabWidth: 50,
29584         preferredTabWidth: 150,
29585         panels: [
29586             new CP("center1", {title: "Close Me", closable: true}),
29587             new CP("center2", {title: "Center Panel", closable: false})
29588         ]
29589     }
29590 }, document.body);
29591
29592 layout.getRegion("center").showPanel("center1");
29593 </code></pre>
29594  * @param config
29595  * @param targetEl
29596  */
29597 Roo.BorderLayout.create = function(config, targetEl){
29598     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29599     layout.beginUpdate();
29600     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29601     for(var j = 0, jlen = regions.length; j < jlen; j++){
29602         var lr = regions[j];
29603         if(layout.regions[lr] && config[lr].panels){
29604             var r = layout.regions[lr];
29605             var ps = config[lr].panels;
29606             layout.addTypedPanels(r, ps);
29607         }
29608     }
29609     layout.endUpdate();
29610     return layout;
29611 };
29612
29613 // private
29614 Roo.BorderLayout.RegionFactory = {
29615     // private
29616     validRegions : ["north","south","east","west","center"],
29617
29618     // private
29619     create : function(target, mgr, config){
29620         target = target.toLowerCase();
29621         if(config.lightweight || config.basic){
29622             return new Roo.BasicLayoutRegion(mgr, config, target);
29623         }
29624         switch(target){
29625             case "north":
29626                 return new Roo.NorthLayoutRegion(mgr, config);
29627             case "south":
29628                 return new Roo.SouthLayoutRegion(mgr, config);
29629             case "east":
29630                 return new Roo.EastLayoutRegion(mgr, config);
29631             case "west":
29632                 return new Roo.WestLayoutRegion(mgr, config);
29633             case "center":
29634                 return new Roo.CenterLayoutRegion(mgr, config);
29635         }
29636         throw 'Layout region "'+target+'" not supported.';
29637     }
29638 };/*
29639  * Based on:
29640  * Ext JS Library 1.1.1
29641  * Copyright(c) 2006-2007, Ext JS, LLC.
29642  *
29643  * Originally Released Under LGPL - original licence link has changed is not relivant.
29644  *
29645  * Fork - LGPL
29646  * <script type="text/javascript">
29647  */
29648  
29649 /**
29650  * @class Roo.BasicLayoutRegion
29651  * @extends Roo.util.Observable
29652  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29653  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29654  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29655  */
29656 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29657     this.mgr = mgr;
29658     this.position  = pos;
29659     this.events = {
29660         /**
29661          * @scope Roo.BasicLayoutRegion
29662          */
29663         
29664         /**
29665          * @event beforeremove
29666          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29667          * @param {Roo.LayoutRegion} this
29668          * @param {Roo.ContentPanel} panel The panel
29669          * @param {Object} e The cancel event object
29670          */
29671         "beforeremove" : true,
29672         /**
29673          * @event invalidated
29674          * Fires when the layout for this region is changed.
29675          * @param {Roo.LayoutRegion} this
29676          */
29677         "invalidated" : true,
29678         /**
29679          * @event visibilitychange
29680          * Fires when this region is shown or hidden 
29681          * @param {Roo.LayoutRegion} this
29682          * @param {Boolean} visibility true or false
29683          */
29684         "visibilitychange" : true,
29685         /**
29686          * @event paneladded
29687          * Fires when a panel is added. 
29688          * @param {Roo.LayoutRegion} this
29689          * @param {Roo.ContentPanel} panel The panel
29690          */
29691         "paneladded" : true,
29692         /**
29693          * @event panelremoved
29694          * Fires when a panel is removed. 
29695          * @param {Roo.LayoutRegion} this
29696          * @param {Roo.ContentPanel} panel The panel
29697          */
29698         "panelremoved" : true,
29699         /**
29700          * @event beforecollapse
29701          * Fires when this region before collapse.
29702          * @param {Roo.LayoutRegion} this
29703          */
29704         "beforecollapse" : true,
29705         /**
29706          * @event collapsed
29707          * Fires when this region is collapsed.
29708          * @param {Roo.LayoutRegion} this
29709          */
29710         "collapsed" : true,
29711         /**
29712          * @event expanded
29713          * Fires when this region is expanded.
29714          * @param {Roo.LayoutRegion} this
29715          */
29716         "expanded" : true,
29717         /**
29718          * @event slideshow
29719          * Fires when this region is slid into view.
29720          * @param {Roo.LayoutRegion} this
29721          */
29722         "slideshow" : true,
29723         /**
29724          * @event slidehide
29725          * Fires when this region slides out of view. 
29726          * @param {Roo.LayoutRegion} this
29727          */
29728         "slidehide" : true,
29729         /**
29730          * @event panelactivated
29731          * Fires when a panel is activated. 
29732          * @param {Roo.LayoutRegion} this
29733          * @param {Roo.ContentPanel} panel The activated panel
29734          */
29735         "panelactivated" : true,
29736         /**
29737          * @event resized
29738          * Fires when the user resizes this region. 
29739          * @param {Roo.LayoutRegion} this
29740          * @param {Number} newSize The new size (width for east/west, height for north/south)
29741          */
29742         "resized" : true
29743     };
29744     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29745     this.panels = new Roo.util.MixedCollection();
29746     this.panels.getKey = this.getPanelId.createDelegate(this);
29747     this.box = null;
29748     this.activePanel = null;
29749     // ensure listeners are added...
29750     
29751     if (config.listeners || config.events) {
29752         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29753             listeners : config.listeners || {},
29754             events : config.events || {}
29755         });
29756     }
29757     
29758     if(skipConfig !== true){
29759         this.applyConfig(config);
29760     }
29761 };
29762
29763 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29764     getPanelId : function(p){
29765         return p.getId();
29766     },
29767     
29768     applyConfig : function(config){
29769         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29770         this.config = config;
29771         
29772     },
29773     
29774     /**
29775      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29776      * the width, for horizontal (north, south) the height.
29777      * @param {Number} newSize The new width or height
29778      */
29779     resizeTo : function(newSize){
29780         var el = this.el ? this.el :
29781                  (this.activePanel ? this.activePanel.getEl() : null);
29782         if(el){
29783             switch(this.position){
29784                 case "east":
29785                 case "west":
29786                     el.setWidth(newSize);
29787                     this.fireEvent("resized", this, newSize);
29788                 break;
29789                 case "north":
29790                 case "south":
29791                     el.setHeight(newSize);
29792                     this.fireEvent("resized", this, newSize);
29793                 break;                
29794             }
29795         }
29796     },
29797     
29798     getBox : function(){
29799         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29800     },
29801     
29802     getMargins : function(){
29803         return this.margins;
29804     },
29805     
29806     updateBox : function(box){
29807         this.box = box;
29808         var el = this.activePanel.getEl();
29809         el.dom.style.left = box.x + "px";
29810         el.dom.style.top = box.y + "px";
29811         this.activePanel.setSize(box.width, box.height);
29812     },
29813     
29814     /**
29815      * Returns the container element for this region.
29816      * @return {Roo.Element}
29817      */
29818     getEl : function(){
29819         return this.activePanel;
29820     },
29821     
29822     /**
29823      * Returns true if this region is currently visible.
29824      * @return {Boolean}
29825      */
29826     isVisible : function(){
29827         return this.activePanel ? true : false;
29828     },
29829     
29830     setActivePanel : function(panel){
29831         panel = this.getPanel(panel);
29832         if(this.activePanel && this.activePanel != panel){
29833             this.activePanel.setActiveState(false);
29834             this.activePanel.getEl().setLeftTop(-10000,-10000);
29835         }
29836         this.activePanel = panel;
29837         panel.setActiveState(true);
29838         if(this.box){
29839             panel.setSize(this.box.width, this.box.height);
29840         }
29841         this.fireEvent("panelactivated", this, panel);
29842         this.fireEvent("invalidated");
29843     },
29844     
29845     /**
29846      * Show the specified panel.
29847      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29848      * @return {Roo.ContentPanel} The shown panel or null
29849      */
29850     showPanel : function(panel){
29851         if(panel = this.getPanel(panel)){
29852             this.setActivePanel(panel);
29853         }
29854         return panel;
29855     },
29856     
29857     /**
29858      * Get the active panel for this region.
29859      * @return {Roo.ContentPanel} The active panel or null
29860      */
29861     getActivePanel : function(){
29862         return this.activePanel;
29863     },
29864     
29865     /**
29866      * Add the passed ContentPanel(s)
29867      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29868      * @return {Roo.ContentPanel} The panel added (if only one was added)
29869      */
29870     add : function(panel){
29871         if(arguments.length > 1){
29872             for(var i = 0, len = arguments.length; i < len; i++) {
29873                 this.add(arguments[i]);
29874             }
29875             return null;
29876         }
29877         if(this.hasPanel(panel)){
29878             this.showPanel(panel);
29879             return panel;
29880         }
29881         var el = panel.getEl();
29882         if(el.dom.parentNode != this.mgr.el.dom){
29883             this.mgr.el.dom.appendChild(el.dom);
29884         }
29885         if(panel.setRegion){
29886             panel.setRegion(this);
29887         }
29888         this.panels.add(panel);
29889         el.setStyle("position", "absolute");
29890         if(!panel.background){
29891             this.setActivePanel(panel);
29892             if(this.config.initialSize && this.panels.getCount()==1){
29893                 this.resizeTo(this.config.initialSize);
29894             }
29895         }
29896         this.fireEvent("paneladded", this, panel);
29897         return panel;
29898     },
29899     
29900     /**
29901      * Returns true if the panel is in this region.
29902      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29903      * @return {Boolean}
29904      */
29905     hasPanel : function(panel){
29906         if(typeof panel == "object"){ // must be panel obj
29907             panel = panel.getId();
29908         }
29909         return this.getPanel(panel) ? true : false;
29910     },
29911     
29912     /**
29913      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29914      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29915      * @param {Boolean} preservePanel Overrides the config preservePanel option
29916      * @return {Roo.ContentPanel} The panel that was removed
29917      */
29918     remove : function(panel, preservePanel){
29919         panel = this.getPanel(panel);
29920         if(!panel){
29921             return null;
29922         }
29923         var e = {};
29924         this.fireEvent("beforeremove", this, panel, e);
29925         if(e.cancel === true){
29926             return null;
29927         }
29928         var panelId = panel.getId();
29929         this.panels.removeKey(panelId);
29930         return panel;
29931     },
29932     
29933     /**
29934      * Returns the panel specified or null if it's not in this region.
29935      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29936      * @return {Roo.ContentPanel}
29937      */
29938     getPanel : function(id){
29939         if(typeof id == "object"){ // must be panel obj
29940             return id;
29941         }
29942         return this.panels.get(id);
29943     },
29944     
29945     /**
29946      * Returns this regions position (north/south/east/west/center).
29947      * @return {String} 
29948      */
29949     getPosition: function(){
29950         return this.position;    
29951     }
29952 });/*
29953  * Based on:
29954  * Ext JS Library 1.1.1
29955  * Copyright(c) 2006-2007, Ext JS, LLC.
29956  *
29957  * Originally Released Under LGPL - original licence link has changed is not relivant.
29958  *
29959  * Fork - LGPL
29960  * <script type="text/javascript">
29961  */
29962  
29963 /**
29964  * @class Roo.LayoutRegion
29965  * @extends Roo.BasicLayoutRegion
29966  * This class represents a region in a layout manager.
29967  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29968  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29969  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29970  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29971  * @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})
29972  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29973  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29974  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29975  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29976  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29977  * @cfg {String}    title           The title for the region (overrides panel titles)
29978  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29979  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29980  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29981  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29982  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29983  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29984  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29985  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29986  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29987  * @cfg {Boolean}   showPin         True to show a pin button
29988  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29989  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29990  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29991  * @cfg {Number}    width           For East/West panels
29992  * @cfg {Number}    height          For North/South panels
29993  * @cfg {Boolean}   split           To show the splitter
29994  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29995  */
29996 Roo.LayoutRegion = function(mgr, config, pos){
29997     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29998     var dh = Roo.DomHelper;
29999     /** This region's container element 
30000     * @type Roo.Element */
30001     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30002     /** This region's title element 
30003     * @type Roo.Element */
30004
30005     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30006         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30007         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30008     ]}, true);
30009     this.titleEl.enableDisplayMode();
30010     /** This region's title text element 
30011     * @type HTMLElement */
30012     this.titleTextEl = this.titleEl.dom.firstChild;
30013     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30014     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30015     this.closeBtn.enableDisplayMode();
30016     this.closeBtn.on("click", this.closeClicked, this);
30017     this.closeBtn.hide();
30018
30019     this.createBody(config);
30020     this.visible = true;
30021     this.collapsed = false;
30022
30023     if(config.hideWhenEmpty){
30024         this.hide();
30025         this.on("paneladded", this.validateVisibility, this);
30026         this.on("panelremoved", this.validateVisibility, this);
30027     }
30028     this.applyConfig(config);
30029 };
30030
30031 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30032
30033     createBody : function(){
30034         /** This region's body element 
30035         * @type Roo.Element */
30036         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30037     },
30038
30039     applyConfig : function(c){
30040         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30041             var dh = Roo.DomHelper;
30042             if(c.titlebar !== false){
30043                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30044                 this.collapseBtn.on("click", this.collapse, this);
30045                 this.collapseBtn.enableDisplayMode();
30046
30047                 if(c.showPin === true || this.showPin){
30048                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30049                     this.stickBtn.enableDisplayMode();
30050                     this.stickBtn.on("click", this.expand, this);
30051                     this.stickBtn.hide();
30052                 }
30053             }
30054             /** This region's collapsed element
30055             * @type Roo.Element */
30056             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30057                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30058             ]}, true);
30059             if(c.floatable !== false){
30060                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30061                this.collapsedEl.on("click", this.collapseClick, this);
30062             }
30063
30064             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30065                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30066                    id: "message", unselectable: "on", style:{"float":"left"}});
30067                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30068              }
30069             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30070             this.expandBtn.on("click", this.expand, this);
30071         }
30072         if(this.collapseBtn){
30073             this.collapseBtn.setVisible(c.collapsible == true);
30074         }
30075         this.cmargins = c.cmargins || this.cmargins ||
30076                          (this.position == "west" || this.position == "east" ?
30077                              {top: 0, left: 2, right:2, bottom: 0} :
30078                              {top: 2, left: 0, right:0, bottom: 2});
30079         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30080         this.bottomTabs = c.tabPosition != "top";
30081         this.autoScroll = c.autoScroll || false;
30082         if(this.autoScroll){
30083             this.bodyEl.setStyle("overflow", "auto");
30084         }else{
30085             this.bodyEl.setStyle("overflow", "hidden");
30086         }
30087         //if(c.titlebar !== false){
30088             if((!c.titlebar && !c.title) || c.titlebar === false){
30089                 this.titleEl.hide();
30090             }else{
30091                 this.titleEl.show();
30092                 if(c.title){
30093                     this.titleTextEl.innerHTML = c.title;
30094                 }
30095             }
30096         //}
30097         this.duration = c.duration || .30;
30098         this.slideDuration = c.slideDuration || .45;
30099         this.config = c;
30100         if(c.collapsed){
30101             this.collapse(true);
30102         }
30103         if(c.hidden){
30104             this.hide();
30105         }
30106     },
30107     /**
30108      * Returns true if this region is currently visible.
30109      * @return {Boolean}
30110      */
30111     isVisible : function(){
30112         return this.visible;
30113     },
30114
30115     /**
30116      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30117      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30118      */
30119     setCollapsedTitle : function(title){
30120         title = title || "&#160;";
30121         if(this.collapsedTitleTextEl){
30122             this.collapsedTitleTextEl.innerHTML = title;
30123         }
30124     },
30125
30126     getBox : function(){
30127         var b;
30128         if(!this.collapsed){
30129             b = this.el.getBox(false, true);
30130         }else{
30131             b = this.collapsedEl.getBox(false, true);
30132         }
30133         return b;
30134     },
30135
30136     getMargins : function(){
30137         return this.collapsed ? this.cmargins : this.margins;
30138     },
30139
30140     highlight : function(){
30141         this.el.addClass("x-layout-panel-dragover");
30142     },
30143
30144     unhighlight : function(){
30145         this.el.removeClass("x-layout-panel-dragover");
30146     },
30147
30148     updateBox : function(box){
30149         this.box = box;
30150         if(!this.collapsed){
30151             this.el.dom.style.left = box.x + "px";
30152             this.el.dom.style.top = box.y + "px";
30153             this.updateBody(box.width, box.height);
30154         }else{
30155             this.collapsedEl.dom.style.left = box.x + "px";
30156             this.collapsedEl.dom.style.top = box.y + "px";
30157             this.collapsedEl.setSize(box.width, box.height);
30158         }
30159         if(this.tabs){
30160             this.tabs.autoSizeTabs();
30161         }
30162     },
30163
30164     updateBody : function(w, h){
30165         if(w !== null){
30166             this.el.setWidth(w);
30167             w -= this.el.getBorderWidth("rl");
30168             if(this.config.adjustments){
30169                 w += this.config.adjustments[0];
30170             }
30171         }
30172         if(h !== null){
30173             this.el.setHeight(h);
30174             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30175             h -= this.el.getBorderWidth("tb");
30176             if(this.config.adjustments){
30177                 h += this.config.adjustments[1];
30178             }
30179             this.bodyEl.setHeight(h);
30180             if(this.tabs){
30181                 h = this.tabs.syncHeight(h);
30182             }
30183         }
30184         if(this.panelSize){
30185             w = w !== null ? w : this.panelSize.width;
30186             h = h !== null ? h : this.panelSize.height;
30187         }
30188         if(this.activePanel){
30189             var el = this.activePanel.getEl();
30190             w = w !== null ? w : el.getWidth();
30191             h = h !== null ? h : el.getHeight();
30192             this.panelSize = {width: w, height: h};
30193             this.activePanel.setSize(w, h);
30194         }
30195         if(Roo.isIE && this.tabs){
30196             this.tabs.el.repaint();
30197         }
30198     },
30199
30200     /**
30201      * Returns the container element for this region.
30202      * @return {Roo.Element}
30203      */
30204     getEl : function(){
30205         return this.el;
30206     },
30207
30208     /**
30209      * Hides this region.
30210      */
30211     hide : function(){
30212         if(!this.collapsed){
30213             this.el.dom.style.left = "-2000px";
30214             this.el.hide();
30215         }else{
30216             this.collapsedEl.dom.style.left = "-2000px";
30217             this.collapsedEl.hide();
30218         }
30219         this.visible = false;
30220         this.fireEvent("visibilitychange", this, false);
30221     },
30222
30223     /**
30224      * Shows this region if it was previously hidden.
30225      */
30226     show : function(){
30227         if(!this.collapsed){
30228             this.el.show();
30229         }else{
30230             this.collapsedEl.show();
30231         }
30232         this.visible = true;
30233         this.fireEvent("visibilitychange", this, true);
30234     },
30235
30236     closeClicked : function(){
30237         if(this.activePanel){
30238             this.remove(this.activePanel);
30239         }
30240     },
30241
30242     collapseClick : function(e){
30243         if(this.isSlid){
30244            e.stopPropagation();
30245            this.slideIn();
30246         }else{
30247            e.stopPropagation();
30248            this.slideOut();
30249         }
30250     },
30251
30252     /**
30253      * Collapses this region.
30254      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30255      */
30256     collapse : function(skipAnim, skipCheck){
30257         if(this.collapsed) {
30258             return;
30259         }
30260         
30261         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30262             
30263             this.collapsed = true;
30264             if(this.split){
30265                 this.split.el.hide();
30266             }
30267             if(this.config.animate && skipAnim !== true){
30268                 this.fireEvent("invalidated", this);
30269                 this.animateCollapse();
30270             }else{
30271                 this.el.setLocation(-20000,-20000);
30272                 this.el.hide();
30273                 this.collapsedEl.show();
30274                 this.fireEvent("collapsed", this);
30275                 this.fireEvent("invalidated", this);
30276             }
30277         }
30278         
30279     },
30280
30281     animateCollapse : function(){
30282         // overridden
30283     },
30284
30285     /**
30286      * Expands this region if it was previously collapsed.
30287      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30288      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30289      */
30290     expand : function(e, skipAnim){
30291         if(e) {
30292             e.stopPropagation();
30293         }
30294         if(!this.collapsed || this.el.hasActiveFx()) {
30295             return;
30296         }
30297         if(this.isSlid){
30298             this.afterSlideIn();
30299             skipAnim = true;
30300         }
30301         this.collapsed = false;
30302         if(this.config.animate && skipAnim !== true){
30303             this.animateExpand();
30304         }else{
30305             this.el.show();
30306             if(this.split){
30307                 this.split.el.show();
30308             }
30309             this.collapsedEl.setLocation(-2000,-2000);
30310             this.collapsedEl.hide();
30311             this.fireEvent("invalidated", this);
30312             this.fireEvent("expanded", this);
30313         }
30314     },
30315
30316     animateExpand : function(){
30317         // overridden
30318     },
30319
30320     initTabs : function()
30321     {
30322         this.bodyEl.setStyle("overflow", "hidden");
30323         var ts = new Roo.TabPanel(
30324                 this.bodyEl.dom,
30325                 {
30326                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30327                     disableTooltips: this.config.disableTabTips,
30328                     toolbar : this.config.toolbar
30329                 }
30330         );
30331         if(this.config.hideTabs){
30332             ts.stripWrap.setDisplayed(false);
30333         }
30334         this.tabs = ts;
30335         ts.resizeTabs = this.config.resizeTabs === true;
30336         ts.minTabWidth = this.config.minTabWidth || 40;
30337         ts.maxTabWidth = this.config.maxTabWidth || 250;
30338         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30339         ts.monitorResize = false;
30340         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30341         ts.bodyEl.addClass('x-layout-tabs-body');
30342         this.panels.each(this.initPanelAsTab, this);
30343     },
30344
30345     initPanelAsTab : function(panel){
30346         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30347                     this.config.closeOnTab && panel.isClosable());
30348         if(panel.tabTip !== undefined){
30349             ti.setTooltip(panel.tabTip);
30350         }
30351         ti.on("activate", function(){
30352               this.setActivePanel(panel);
30353         }, this);
30354         if(this.config.closeOnTab){
30355             ti.on("beforeclose", function(t, e){
30356                 e.cancel = true;
30357                 this.remove(panel);
30358             }, this);
30359         }
30360         return ti;
30361     },
30362
30363     updatePanelTitle : function(panel, title){
30364         if(this.activePanel == panel){
30365             this.updateTitle(title);
30366         }
30367         if(this.tabs){
30368             var ti = this.tabs.getTab(panel.getEl().id);
30369             ti.setText(title);
30370             if(panel.tabTip !== undefined){
30371                 ti.setTooltip(panel.tabTip);
30372             }
30373         }
30374     },
30375
30376     updateTitle : function(title){
30377         if(this.titleTextEl && !this.config.title){
30378             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30379         }
30380     },
30381
30382     setActivePanel : function(panel){
30383         panel = this.getPanel(panel);
30384         if(this.activePanel && this.activePanel != panel){
30385             this.activePanel.setActiveState(false);
30386         }
30387         this.activePanel = panel;
30388         panel.setActiveState(true);
30389         if(this.panelSize){
30390             panel.setSize(this.panelSize.width, this.panelSize.height);
30391         }
30392         if(this.closeBtn){
30393             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30394         }
30395         this.updateTitle(panel.getTitle());
30396         if(this.tabs){
30397             this.fireEvent("invalidated", this);
30398         }
30399         this.fireEvent("panelactivated", this, panel);
30400     },
30401
30402     /**
30403      * Shows the specified panel.
30404      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30405      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30406      */
30407     showPanel : function(panel)
30408     {
30409         panel = this.getPanel(panel);
30410         if(panel){
30411             if(this.tabs){
30412                 var tab = this.tabs.getTab(panel.getEl().id);
30413                 if(tab.isHidden()){
30414                     this.tabs.unhideTab(tab.id);
30415                 }
30416                 tab.activate();
30417             }else{
30418                 this.setActivePanel(panel);
30419             }
30420         }
30421         return panel;
30422     },
30423
30424     /**
30425      * Get the active panel for this region.
30426      * @return {Roo.ContentPanel} The active panel or null
30427      */
30428     getActivePanel : function(){
30429         return this.activePanel;
30430     },
30431
30432     validateVisibility : function(){
30433         if(this.panels.getCount() < 1){
30434             this.updateTitle("&#160;");
30435             this.closeBtn.hide();
30436             this.hide();
30437         }else{
30438             if(!this.isVisible()){
30439                 this.show();
30440             }
30441         }
30442     },
30443
30444     /**
30445      * Adds the passed ContentPanel(s) to this region.
30446      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30447      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30448      */
30449     add : function(panel){
30450         if(arguments.length > 1){
30451             for(var i = 0, len = arguments.length; i < len; i++) {
30452                 this.add(arguments[i]);
30453             }
30454             return null;
30455         }
30456         if(this.hasPanel(panel)){
30457             this.showPanel(panel);
30458             return panel;
30459         }
30460         panel.setRegion(this);
30461         this.panels.add(panel);
30462         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30463             this.bodyEl.dom.appendChild(panel.getEl().dom);
30464             if(panel.background !== true){
30465                 this.setActivePanel(panel);
30466             }
30467             this.fireEvent("paneladded", this, panel);
30468             return panel;
30469         }
30470         if(!this.tabs){
30471             this.initTabs();
30472         }else{
30473             this.initPanelAsTab(panel);
30474         }
30475         if(panel.background !== true){
30476             this.tabs.activate(panel.getEl().id);
30477         }
30478         this.fireEvent("paneladded", this, panel);
30479         return panel;
30480     },
30481
30482     /**
30483      * Hides the tab for the specified panel.
30484      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30485      */
30486     hidePanel : function(panel){
30487         if(this.tabs && (panel = this.getPanel(panel))){
30488             this.tabs.hideTab(panel.getEl().id);
30489         }
30490     },
30491
30492     /**
30493      * Unhides the tab for a previously hidden panel.
30494      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30495      */
30496     unhidePanel : function(panel){
30497         if(this.tabs && (panel = this.getPanel(panel))){
30498             this.tabs.unhideTab(panel.getEl().id);
30499         }
30500     },
30501
30502     clearPanels : function(){
30503         while(this.panels.getCount() > 0){
30504              this.remove(this.panels.first());
30505         }
30506     },
30507
30508     /**
30509      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30510      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30511      * @param {Boolean} preservePanel Overrides the config preservePanel option
30512      * @return {Roo.ContentPanel} The panel that was removed
30513      */
30514     remove : function(panel, preservePanel){
30515         panel = this.getPanel(panel);
30516         if(!panel){
30517             return null;
30518         }
30519         var e = {};
30520         this.fireEvent("beforeremove", this, panel, e);
30521         if(e.cancel === true){
30522             return null;
30523         }
30524         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30525         var panelId = panel.getId();
30526         this.panels.removeKey(panelId);
30527         if(preservePanel){
30528             document.body.appendChild(panel.getEl().dom);
30529         }
30530         if(this.tabs){
30531             this.tabs.removeTab(panel.getEl().id);
30532         }else if (!preservePanel){
30533             this.bodyEl.dom.removeChild(panel.getEl().dom);
30534         }
30535         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30536             var p = this.panels.first();
30537             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30538             tempEl.appendChild(p.getEl().dom);
30539             this.bodyEl.update("");
30540             this.bodyEl.dom.appendChild(p.getEl().dom);
30541             tempEl = null;
30542             this.updateTitle(p.getTitle());
30543             this.tabs = null;
30544             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30545             this.setActivePanel(p);
30546         }
30547         panel.setRegion(null);
30548         if(this.activePanel == panel){
30549             this.activePanel = null;
30550         }
30551         if(this.config.autoDestroy !== false && preservePanel !== true){
30552             try{panel.destroy();}catch(e){}
30553         }
30554         this.fireEvent("panelremoved", this, panel);
30555         return panel;
30556     },
30557
30558     /**
30559      * Returns the TabPanel component used by this region
30560      * @return {Roo.TabPanel}
30561      */
30562     getTabs : function(){
30563         return this.tabs;
30564     },
30565
30566     createTool : function(parentEl, className){
30567         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30568             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30569         btn.addClassOnOver("x-layout-tools-button-over");
30570         return btn;
30571     }
30572 });/*
30573  * Based on:
30574  * Ext JS Library 1.1.1
30575  * Copyright(c) 2006-2007, Ext JS, LLC.
30576  *
30577  * Originally Released Under LGPL - original licence link has changed is not relivant.
30578  *
30579  * Fork - LGPL
30580  * <script type="text/javascript">
30581  */
30582  
30583
30584
30585 /**
30586  * @class Roo.SplitLayoutRegion
30587  * @extends Roo.LayoutRegion
30588  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30589  */
30590 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30591     this.cursor = cursor;
30592     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30593 };
30594
30595 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30596     splitTip : "Drag to resize.",
30597     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30598     useSplitTips : false,
30599
30600     applyConfig : function(config){
30601         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30602         if(config.split){
30603             if(!this.split){
30604                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30605                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30606                 /** The SplitBar for this region 
30607                 * @type Roo.SplitBar */
30608                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30609                 this.split.on("moved", this.onSplitMove, this);
30610                 this.split.useShim = config.useShim === true;
30611                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30612                 if(this.useSplitTips){
30613                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30614                 }
30615                 if(config.collapsible){
30616                     this.split.el.on("dblclick", this.collapse,  this);
30617                 }
30618             }
30619             if(typeof config.minSize != "undefined"){
30620                 this.split.minSize = config.minSize;
30621             }
30622             if(typeof config.maxSize != "undefined"){
30623                 this.split.maxSize = config.maxSize;
30624             }
30625             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30626                 this.hideSplitter();
30627             }
30628         }
30629     },
30630
30631     getHMaxSize : function(){
30632          var cmax = this.config.maxSize || 10000;
30633          var center = this.mgr.getRegion("center");
30634          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30635     },
30636
30637     getVMaxSize : function(){
30638          var cmax = this.config.maxSize || 10000;
30639          var center = this.mgr.getRegion("center");
30640          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30641     },
30642
30643     onSplitMove : function(split, newSize){
30644         this.fireEvent("resized", this, newSize);
30645     },
30646     
30647     /** 
30648      * Returns the {@link Roo.SplitBar} for this region.
30649      * @return {Roo.SplitBar}
30650      */
30651     getSplitBar : function(){
30652         return this.split;
30653     },
30654     
30655     hide : function(){
30656         this.hideSplitter();
30657         Roo.SplitLayoutRegion.superclass.hide.call(this);
30658     },
30659
30660     hideSplitter : function(){
30661         if(this.split){
30662             this.split.el.setLocation(-2000,-2000);
30663             this.split.el.hide();
30664         }
30665     },
30666
30667     show : function(){
30668         if(this.split){
30669             this.split.el.show();
30670         }
30671         Roo.SplitLayoutRegion.superclass.show.call(this);
30672     },
30673     
30674     beforeSlide: function(){
30675         if(Roo.isGecko){// firefox overflow auto bug workaround
30676             this.bodyEl.clip();
30677             if(this.tabs) {
30678                 this.tabs.bodyEl.clip();
30679             }
30680             if(this.activePanel){
30681                 this.activePanel.getEl().clip();
30682                 
30683                 if(this.activePanel.beforeSlide){
30684                     this.activePanel.beforeSlide();
30685                 }
30686             }
30687         }
30688     },
30689     
30690     afterSlide : function(){
30691         if(Roo.isGecko){// firefox overflow auto bug workaround
30692             this.bodyEl.unclip();
30693             if(this.tabs) {
30694                 this.tabs.bodyEl.unclip();
30695             }
30696             if(this.activePanel){
30697                 this.activePanel.getEl().unclip();
30698                 if(this.activePanel.afterSlide){
30699                     this.activePanel.afterSlide();
30700                 }
30701             }
30702         }
30703     },
30704
30705     initAutoHide : function(){
30706         if(this.autoHide !== false){
30707             if(!this.autoHideHd){
30708                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30709                 this.autoHideHd = {
30710                     "mouseout": function(e){
30711                         if(!e.within(this.el, true)){
30712                             st.delay(500);
30713                         }
30714                     },
30715                     "mouseover" : function(e){
30716                         st.cancel();
30717                     },
30718                     scope : this
30719                 };
30720             }
30721             this.el.on(this.autoHideHd);
30722         }
30723     },
30724
30725     clearAutoHide : function(){
30726         if(this.autoHide !== false){
30727             this.el.un("mouseout", this.autoHideHd.mouseout);
30728             this.el.un("mouseover", this.autoHideHd.mouseover);
30729         }
30730     },
30731
30732     clearMonitor : function(){
30733         Roo.get(document).un("click", this.slideInIf, this);
30734     },
30735
30736     // these names are backwards but not changed for compat
30737     slideOut : function(){
30738         if(this.isSlid || this.el.hasActiveFx()){
30739             return;
30740         }
30741         this.isSlid = true;
30742         if(this.collapseBtn){
30743             this.collapseBtn.hide();
30744         }
30745         this.closeBtnState = this.closeBtn.getStyle('display');
30746         this.closeBtn.hide();
30747         if(this.stickBtn){
30748             this.stickBtn.show();
30749         }
30750         this.el.show();
30751         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30752         this.beforeSlide();
30753         this.el.setStyle("z-index", 10001);
30754         this.el.slideIn(this.getSlideAnchor(), {
30755             callback: function(){
30756                 this.afterSlide();
30757                 this.initAutoHide();
30758                 Roo.get(document).on("click", this.slideInIf, this);
30759                 this.fireEvent("slideshow", this);
30760             },
30761             scope: this,
30762             block: true
30763         });
30764     },
30765
30766     afterSlideIn : function(){
30767         this.clearAutoHide();
30768         this.isSlid = false;
30769         this.clearMonitor();
30770         this.el.setStyle("z-index", "");
30771         if(this.collapseBtn){
30772             this.collapseBtn.show();
30773         }
30774         this.closeBtn.setStyle('display', this.closeBtnState);
30775         if(this.stickBtn){
30776             this.stickBtn.hide();
30777         }
30778         this.fireEvent("slidehide", this);
30779     },
30780
30781     slideIn : function(cb){
30782         if(!this.isSlid || this.el.hasActiveFx()){
30783             Roo.callback(cb);
30784             return;
30785         }
30786         this.isSlid = false;
30787         this.beforeSlide();
30788         this.el.slideOut(this.getSlideAnchor(), {
30789             callback: function(){
30790                 this.el.setLeftTop(-10000, -10000);
30791                 this.afterSlide();
30792                 this.afterSlideIn();
30793                 Roo.callback(cb);
30794             },
30795             scope: this,
30796             block: true
30797         });
30798     },
30799     
30800     slideInIf : function(e){
30801         if(!e.within(this.el)){
30802             this.slideIn();
30803         }
30804     },
30805
30806     animateCollapse : function(){
30807         this.beforeSlide();
30808         this.el.setStyle("z-index", 20000);
30809         var anchor = this.getSlideAnchor();
30810         this.el.slideOut(anchor, {
30811             callback : function(){
30812                 this.el.setStyle("z-index", "");
30813                 this.collapsedEl.slideIn(anchor, {duration:.3});
30814                 this.afterSlide();
30815                 this.el.setLocation(-10000,-10000);
30816                 this.el.hide();
30817                 this.fireEvent("collapsed", this);
30818             },
30819             scope: this,
30820             block: true
30821         });
30822     },
30823
30824     animateExpand : function(){
30825         this.beforeSlide();
30826         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30827         this.el.setStyle("z-index", 20000);
30828         this.collapsedEl.hide({
30829             duration:.1
30830         });
30831         this.el.slideIn(this.getSlideAnchor(), {
30832             callback : function(){
30833                 this.el.setStyle("z-index", "");
30834                 this.afterSlide();
30835                 if(this.split){
30836                     this.split.el.show();
30837                 }
30838                 this.fireEvent("invalidated", this);
30839                 this.fireEvent("expanded", this);
30840             },
30841             scope: this,
30842             block: true
30843         });
30844     },
30845
30846     anchors : {
30847         "west" : "left",
30848         "east" : "right",
30849         "north" : "top",
30850         "south" : "bottom"
30851     },
30852
30853     sanchors : {
30854         "west" : "l",
30855         "east" : "r",
30856         "north" : "t",
30857         "south" : "b"
30858     },
30859
30860     canchors : {
30861         "west" : "tl-tr",
30862         "east" : "tr-tl",
30863         "north" : "tl-bl",
30864         "south" : "bl-tl"
30865     },
30866
30867     getAnchor : function(){
30868         return this.anchors[this.position];
30869     },
30870
30871     getCollapseAnchor : function(){
30872         return this.canchors[this.position];
30873     },
30874
30875     getSlideAnchor : function(){
30876         return this.sanchors[this.position];
30877     },
30878
30879     getAlignAdj : function(){
30880         var cm = this.cmargins;
30881         switch(this.position){
30882             case "west":
30883                 return [0, 0];
30884             break;
30885             case "east":
30886                 return [0, 0];
30887             break;
30888             case "north":
30889                 return [0, 0];
30890             break;
30891             case "south":
30892                 return [0, 0];
30893             break;
30894         }
30895     },
30896
30897     getExpandAdj : function(){
30898         var c = this.collapsedEl, cm = this.cmargins;
30899         switch(this.position){
30900             case "west":
30901                 return [-(cm.right+c.getWidth()+cm.left), 0];
30902             break;
30903             case "east":
30904                 return [cm.right+c.getWidth()+cm.left, 0];
30905             break;
30906             case "north":
30907                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30908             break;
30909             case "south":
30910                 return [0, cm.top+cm.bottom+c.getHeight()];
30911             break;
30912         }
30913     }
30914 });/*
30915  * Based on:
30916  * Ext JS Library 1.1.1
30917  * Copyright(c) 2006-2007, Ext JS, LLC.
30918  *
30919  * Originally Released Under LGPL - original licence link has changed is not relivant.
30920  *
30921  * Fork - LGPL
30922  * <script type="text/javascript">
30923  */
30924 /*
30925  * These classes are private internal classes
30926  */
30927 Roo.CenterLayoutRegion = function(mgr, config){
30928     Roo.LayoutRegion.call(this, mgr, config, "center");
30929     this.visible = true;
30930     this.minWidth = config.minWidth || 20;
30931     this.minHeight = config.minHeight || 20;
30932 };
30933
30934 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30935     hide : function(){
30936         // center panel can't be hidden
30937     },
30938     
30939     show : function(){
30940         // center panel can't be hidden
30941     },
30942     
30943     getMinWidth: function(){
30944         return this.minWidth;
30945     },
30946     
30947     getMinHeight: function(){
30948         return this.minHeight;
30949     }
30950 });
30951
30952
30953 Roo.NorthLayoutRegion = function(mgr, config){
30954     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30955     if(this.split){
30956         this.split.placement = Roo.SplitBar.TOP;
30957         this.split.orientation = Roo.SplitBar.VERTICAL;
30958         this.split.el.addClass("x-layout-split-v");
30959     }
30960     var size = config.initialSize || config.height;
30961     if(typeof size != "undefined"){
30962         this.el.setHeight(size);
30963     }
30964 };
30965 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30966     orientation: Roo.SplitBar.VERTICAL,
30967     getBox : function(){
30968         if(this.collapsed){
30969             return this.collapsedEl.getBox();
30970         }
30971         var box = this.el.getBox();
30972         if(this.split){
30973             box.height += this.split.el.getHeight();
30974         }
30975         return box;
30976     },
30977     
30978     updateBox : function(box){
30979         if(this.split && !this.collapsed){
30980             box.height -= this.split.el.getHeight();
30981             this.split.el.setLeft(box.x);
30982             this.split.el.setTop(box.y+box.height);
30983             this.split.el.setWidth(box.width);
30984         }
30985         if(this.collapsed){
30986             this.updateBody(box.width, null);
30987         }
30988         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30989     }
30990 });
30991
30992 Roo.SouthLayoutRegion = function(mgr, config){
30993     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30994     if(this.split){
30995         this.split.placement = Roo.SplitBar.BOTTOM;
30996         this.split.orientation = Roo.SplitBar.VERTICAL;
30997         this.split.el.addClass("x-layout-split-v");
30998     }
30999     var size = config.initialSize || config.height;
31000     if(typeof size != "undefined"){
31001         this.el.setHeight(size);
31002     }
31003 };
31004 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31005     orientation: Roo.SplitBar.VERTICAL,
31006     getBox : function(){
31007         if(this.collapsed){
31008             return this.collapsedEl.getBox();
31009         }
31010         var box = this.el.getBox();
31011         if(this.split){
31012             var sh = this.split.el.getHeight();
31013             box.height += sh;
31014             box.y -= sh;
31015         }
31016         return box;
31017     },
31018     
31019     updateBox : function(box){
31020         if(this.split && !this.collapsed){
31021             var sh = this.split.el.getHeight();
31022             box.height -= sh;
31023             box.y += sh;
31024             this.split.el.setLeft(box.x);
31025             this.split.el.setTop(box.y-sh);
31026             this.split.el.setWidth(box.width);
31027         }
31028         if(this.collapsed){
31029             this.updateBody(box.width, null);
31030         }
31031         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31032     }
31033 });
31034
31035 Roo.EastLayoutRegion = function(mgr, config){
31036     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31037     if(this.split){
31038         this.split.placement = Roo.SplitBar.RIGHT;
31039         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31040         this.split.el.addClass("x-layout-split-h");
31041     }
31042     var size = config.initialSize || config.width;
31043     if(typeof size != "undefined"){
31044         this.el.setWidth(size);
31045     }
31046 };
31047 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31048     orientation: Roo.SplitBar.HORIZONTAL,
31049     getBox : function(){
31050         if(this.collapsed){
31051             return this.collapsedEl.getBox();
31052         }
31053         var box = this.el.getBox();
31054         if(this.split){
31055             var sw = this.split.el.getWidth();
31056             box.width += sw;
31057             box.x -= sw;
31058         }
31059         return box;
31060     },
31061
31062     updateBox : function(box){
31063         if(this.split && !this.collapsed){
31064             var sw = this.split.el.getWidth();
31065             box.width -= sw;
31066             this.split.el.setLeft(box.x);
31067             this.split.el.setTop(box.y);
31068             this.split.el.setHeight(box.height);
31069             box.x += sw;
31070         }
31071         if(this.collapsed){
31072             this.updateBody(null, box.height);
31073         }
31074         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31075     }
31076 });
31077
31078 Roo.WestLayoutRegion = function(mgr, config){
31079     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31080     if(this.split){
31081         this.split.placement = Roo.SplitBar.LEFT;
31082         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31083         this.split.el.addClass("x-layout-split-h");
31084     }
31085     var size = config.initialSize || config.width;
31086     if(typeof size != "undefined"){
31087         this.el.setWidth(size);
31088     }
31089 };
31090 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31091     orientation: Roo.SplitBar.HORIZONTAL,
31092     getBox : function(){
31093         if(this.collapsed){
31094             return this.collapsedEl.getBox();
31095         }
31096         var box = this.el.getBox();
31097         if(this.split){
31098             box.width += this.split.el.getWidth();
31099         }
31100         return box;
31101     },
31102     
31103     updateBox : function(box){
31104         if(this.split && !this.collapsed){
31105             var sw = this.split.el.getWidth();
31106             box.width -= sw;
31107             this.split.el.setLeft(box.x+box.width);
31108             this.split.el.setTop(box.y);
31109             this.split.el.setHeight(box.height);
31110         }
31111         if(this.collapsed){
31112             this.updateBody(null, box.height);
31113         }
31114         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31115     }
31116 });
31117 /*
31118  * Based on:
31119  * Ext JS Library 1.1.1
31120  * Copyright(c) 2006-2007, Ext JS, LLC.
31121  *
31122  * Originally Released Under LGPL - original licence link has changed is not relivant.
31123  *
31124  * Fork - LGPL
31125  * <script type="text/javascript">
31126  */
31127  
31128  
31129 /*
31130  * Private internal class for reading and applying state
31131  */
31132 Roo.LayoutStateManager = function(layout){
31133      // default empty state
31134      this.state = {
31135         north: {},
31136         south: {},
31137         east: {},
31138         west: {}       
31139     };
31140 };
31141
31142 Roo.LayoutStateManager.prototype = {
31143     init : function(layout, provider){
31144         this.provider = provider;
31145         var state = provider.get(layout.id+"-layout-state");
31146         if(state){
31147             var wasUpdating = layout.isUpdating();
31148             if(!wasUpdating){
31149                 layout.beginUpdate();
31150             }
31151             for(var key in state){
31152                 if(typeof state[key] != "function"){
31153                     var rstate = state[key];
31154                     var r = layout.getRegion(key);
31155                     if(r && rstate){
31156                         if(rstate.size){
31157                             r.resizeTo(rstate.size);
31158                         }
31159                         if(rstate.collapsed == true){
31160                             r.collapse(true);
31161                         }else{
31162                             r.expand(null, true);
31163                         }
31164                     }
31165                 }
31166             }
31167             if(!wasUpdating){
31168                 layout.endUpdate();
31169             }
31170             this.state = state; 
31171         }
31172         this.layout = layout;
31173         layout.on("regionresized", this.onRegionResized, this);
31174         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31175         layout.on("regionexpanded", this.onRegionExpanded, this);
31176     },
31177     
31178     storeState : function(){
31179         this.provider.set(this.layout.id+"-layout-state", this.state);
31180     },
31181     
31182     onRegionResized : function(region, newSize){
31183         this.state[region.getPosition()].size = newSize;
31184         this.storeState();
31185     },
31186     
31187     onRegionCollapsed : function(region){
31188         this.state[region.getPosition()].collapsed = true;
31189         this.storeState();
31190     },
31191     
31192     onRegionExpanded : function(region){
31193         this.state[region.getPosition()].collapsed = false;
31194         this.storeState();
31195     }
31196 };/*
31197  * Based on:
31198  * Ext JS Library 1.1.1
31199  * Copyright(c) 2006-2007, Ext JS, LLC.
31200  *
31201  * Originally Released Under LGPL - original licence link has changed is not relivant.
31202  *
31203  * Fork - LGPL
31204  * <script type="text/javascript">
31205  */
31206 /**
31207  * @class Roo.ContentPanel
31208  * @extends Roo.util.Observable
31209  * A basic ContentPanel element.
31210  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31211  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31212  * @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
31213  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31214  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31215  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31216  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31217  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31218  * @cfg {String} title          The title for this panel
31219  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31220  * @cfg {String} url            Calls {@link #setUrl} with this value
31221  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31222  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31223  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31224  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31225  * @cfg {String}    style  Extra style to add to the content panel 
31226
31227  * @constructor
31228  * Create a new ContentPanel.
31229  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31230  * @param {String/Object} config A string to set only the title or a config object
31231  * @param {String} content (optional) Set the HTML content for this panel
31232  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31233  */
31234 Roo.ContentPanel = function(el, config, content){
31235     
31236      
31237     /*
31238     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31239         config = el;
31240         el = Roo.id();
31241     }
31242     if (config && config.parentLayout) { 
31243         el = config.parentLayout.el.createChild(); 
31244     }
31245     */
31246     if(el.autoCreate){ // xtype is available if this is called from factory
31247         config = el;
31248         el = Roo.id();
31249     }
31250     this.el = Roo.get(el);
31251     if(!this.el && config && config.autoCreate){
31252         if(typeof config.autoCreate == "object"){
31253             if(!config.autoCreate.id){
31254                 config.autoCreate.id = config.id||el;
31255             }
31256             this.el = Roo.DomHelper.append(document.body,
31257                         config.autoCreate, true);
31258         }else{
31259             this.el = Roo.DomHelper.append(document.body,
31260                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31261         }
31262     }
31263     
31264     
31265     this.closable = false;
31266     this.loaded = false;
31267     this.active = false;
31268     if(typeof config == "string"){
31269         this.title = config;
31270     }else{
31271         Roo.apply(this, config);
31272     }
31273     
31274     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31275         this.wrapEl = this.el.wrap();
31276         this.toolbar.container = this.el.insertSibling(false, 'before');
31277         this.toolbar = new Roo.Toolbar(this.toolbar);
31278     }
31279     
31280     // xtype created footer. - not sure if will work as we normally have to render first..
31281     if (this.footer && !this.footer.el && this.footer.xtype) {
31282         if (!this.wrapEl) {
31283             this.wrapEl = this.el.wrap();
31284         }
31285     
31286         this.footer.container = this.wrapEl.createChild();
31287          
31288         this.footer = Roo.factory(this.footer, Roo);
31289         
31290     }
31291     
31292     if(this.resizeEl){
31293         this.resizeEl = Roo.get(this.resizeEl, true);
31294     }else{
31295         this.resizeEl = this.el;
31296     }
31297     // handle view.xtype
31298     
31299  
31300     
31301     
31302     this.addEvents({
31303         /**
31304          * @event activate
31305          * Fires when this panel is activated. 
31306          * @param {Roo.ContentPanel} this
31307          */
31308         "activate" : true,
31309         /**
31310          * @event deactivate
31311          * Fires when this panel is activated. 
31312          * @param {Roo.ContentPanel} this
31313          */
31314         "deactivate" : true,
31315
31316         /**
31317          * @event resize
31318          * Fires when this panel is resized if fitToFrame is true.
31319          * @param {Roo.ContentPanel} this
31320          * @param {Number} width The width after any component adjustments
31321          * @param {Number} height The height after any component adjustments
31322          */
31323         "resize" : true,
31324         
31325          /**
31326          * @event render
31327          * Fires when this tab is created
31328          * @param {Roo.ContentPanel} this
31329          */
31330         "render" : true
31331          
31332         
31333     });
31334     
31335
31336     
31337     
31338     if(this.autoScroll){
31339         this.resizeEl.setStyle("overflow", "auto");
31340     } else {
31341         // fix randome scrolling
31342         this.el.on('scroll', function() {
31343             Roo.log('fix random scolling');
31344             this.scrollTo('top',0); 
31345         });
31346     }
31347     content = content || this.content;
31348     if(content){
31349         this.setContent(content);
31350     }
31351     if(config && config.url){
31352         this.setUrl(this.url, this.params, this.loadOnce);
31353     }
31354     
31355     
31356     
31357     Roo.ContentPanel.superclass.constructor.call(this);
31358     
31359     if (this.view && typeof(this.view.xtype) != 'undefined') {
31360         this.view.el = this.el.appendChild(document.createElement("div"));
31361         this.view = Roo.factory(this.view); 
31362         this.view.render  &&  this.view.render(false, '');  
31363     }
31364     
31365     
31366     this.fireEvent('render', this);
31367 };
31368
31369 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31370     tabTip:'',
31371     setRegion : function(region){
31372         this.region = region;
31373         if(region){
31374            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31375         }else{
31376            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31377         } 
31378     },
31379     
31380     /**
31381      * Returns the toolbar for this Panel if one was configured. 
31382      * @return {Roo.Toolbar} 
31383      */
31384     getToolbar : function(){
31385         return this.toolbar;
31386     },
31387     
31388     setActiveState : function(active){
31389         this.active = active;
31390         if(!active){
31391             this.fireEvent("deactivate", this);
31392         }else{
31393             this.fireEvent("activate", this);
31394         }
31395     },
31396     /**
31397      * Updates this panel's element
31398      * @param {String} content The new content
31399      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31400     */
31401     setContent : function(content, loadScripts){
31402         this.el.update(content, loadScripts);
31403     },
31404
31405     ignoreResize : function(w, h){
31406         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31407             return true;
31408         }else{
31409             this.lastSize = {width: w, height: h};
31410             return false;
31411         }
31412     },
31413     /**
31414      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31415      * @return {Roo.UpdateManager} The UpdateManager
31416      */
31417     getUpdateManager : function(){
31418         return this.el.getUpdateManager();
31419     },
31420      /**
31421      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31422      * @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:
31423 <pre><code>
31424 panel.load({
31425     url: "your-url.php",
31426     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31427     callback: yourFunction,
31428     scope: yourObject, //(optional scope)
31429     discardUrl: false,
31430     nocache: false,
31431     text: "Loading...",
31432     timeout: 30,
31433     scripts: false
31434 });
31435 </code></pre>
31436      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31437      * 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.
31438      * @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}
31439      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31440      * @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.
31441      * @return {Roo.ContentPanel} this
31442      */
31443     load : function(){
31444         var um = this.el.getUpdateManager();
31445         um.update.apply(um, arguments);
31446         return this;
31447     },
31448
31449
31450     /**
31451      * 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.
31452      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31453      * @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)
31454      * @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)
31455      * @return {Roo.UpdateManager} The UpdateManager
31456      */
31457     setUrl : function(url, params, loadOnce){
31458         if(this.refreshDelegate){
31459             this.removeListener("activate", this.refreshDelegate);
31460         }
31461         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31462         this.on("activate", this.refreshDelegate);
31463         return this.el.getUpdateManager();
31464     },
31465     
31466     _handleRefresh : function(url, params, loadOnce){
31467         if(!loadOnce || !this.loaded){
31468             var updater = this.el.getUpdateManager();
31469             updater.update(url, params, this._setLoaded.createDelegate(this));
31470         }
31471     },
31472     
31473     _setLoaded : function(){
31474         this.loaded = true;
31475     }, 
31476     
31477     /**
31478      * Returns this panel's id
31479      * @return {String} 
31480      */
31481     getId : function(){
31482         return this.el.id;
31483     },
31484     
31485     /** 
31486      * Returns this panel's element - used by regiosn to add.
31487      * @return {Roo.Element} 
31488      */
31489     getEl : function(){
31490         return this.wrapEl || this.el;
31491     },
31492     
31493     adjustForComponents : function(width, height)
31494     {
31495         //Roo.log('adjustForComponents ');
31496         if(this.resizeEl != this.el){
31497             width -= this.el.getFrameWidth('lr');
31498             height -= this.el.getFrameWidth('tb');
31499         }
31500         if(this.toolbar){
31501             var te = this.toolbar.getEl();
31502             height -= te.getHeight();
31503             te.setWidth(width);
31504         }
31505         if(this.footer){
31506             var te = this.footer.getEl();
31507             //Roo.log("footer:" + te.getHeight());
31508             
31509             height -= te.getHeight();
31510             te.setWidth(width);
31511         }
31512         
31513         
31514         if(this.adjustments){
31515             width += this.adjustments[0];
31516             height += this.adjustments[1];
31517         }
31518         return {"width": width, "height": height};
31519     },
31520     
31521     setSize : function(width, height){
31522         if(this.fitToFrame && !this.ignoreResize(width, height)){
31523             if(this.fitContainer && this.resizeEl != this.el){
31524                 this.el.setSize(width, height);
31525             }
31526             var size = this.adjustForComponents(width, height);
31527             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31528             this.fireEvent('resize', this, size.width, size.height);
31529         }
31530     },
31531     
31532     /**
31533      * Returns this panel's title
31534      * @return {String} 
31535      */
31536     getTitle : function(){
31537         return this.title;
31538     },
31539     
31540     /**
31541      * Set this panel's title
31542      * @param {String} title
31543      */
31544     setTitle : function(title){
31545         this.title = title;
31546         if(this.region){
31547             this.region.updatePanelTitle(this, title);
31548         }
31549     },
31550     
31551     /**
31552      * Returns true is this panel was configured to be closable
31553      * @return {Boolean} 
31554      */
31555     isClosable : function(){
31556         return this.closable;
31557     },
31558     
31559     beforeSlide : function(){
31560         this.el.clip();
31561         this.resizeEl.clip();
31562     },
31563     
31564     afterSlide : function(){
31565         this.el.unclip();
31566         this.resizeEl.unclip();
31567     },
31568     
31569     /**
31570      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31571      *   Will fail silently if the {@link #setUrl} method has not been called.
31572      *   This does not activate the panel, just updates its content.
31573      */
31574     refresh : function(){
31575         if(this.refreshDelegate){
31576            this.loaded = false;
31577            this.refreshDelegate();
31578         }
31579     },
31580     
31581     /**
31582      * Destroys this panel
31583      */
31584     destroy : function(){
31585         this.el.removeAllListeners();
31586         var tempEl = document.createElement("span");
31587         tempEl.appendChild(this.el.dom);
31588         tempEl.innerHTML = "";
31589         this.el.remove();
31590         this.el = null;
31591     },
31592     
31593     /**
31594      * form - if the content panel contains a form - this is a reference to it.
31595      * @type {Roo.form.Form}
31596      */
31597     form : false,
31598     /**
31599      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31600      *    This contains a reference to it.
31601      * @type {Roo.View}
31602      */
31603     view : false,
31604     
31605       /**
31606      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31607      * <pre><code>
31608
31609 layout.addxtype({
31610        xtype : 'Form',
31611        items: [ .... ]
31612    }
31613 );
31614
31615 </code></pre>
31616      * @param {Object} cfg Xtype definition of item to add.
31617      */
31618     
31619     addxtype : function(cfg) {
31620         // add form..
31621         if (cfg.xtype.match(/^Form$/)) {
31622             
31623             var el;
31624             //if (this.footer) {
31625             //    el = this.footer.container.insertSibling(false, 'before');
31626             //} else {
31627                 el = this.el.createChild();
31628             //}
31629
31630             this.form = new  Roo.form.Form(cfg);
31631             
31632             
31633             if ( this.form.allItems.length) {
31634                 this.form.render(el.dom);
31635             }
31636             return this.form;
31637         }
31638         // should only have one of theses..
31639         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31640             // views.. should not be just added - used named prop 'view''
31641             
31642             cfg.el = this.el.appendChild(document.createElement("div"));
31643             // factory?
31644             
31645             var ret = new Roo.factory(cfg);
31646              
31647              ret.render && ret.render(false, ''); // render blank..
31648             this.view = ret;
31649             return ret;
31650         }
31651         return false;
31652     }
31653 });
31654
31655 /**
31656  * @class Roo.GridPanel
31657  * @extends Roo.ContentPanel
31658  * @constructor
31659  * Create a new GridPanel.
31660  * @param {Roo.grid.Grid} grid The grid for this panel
31661  * @param {String/Object} config A string to set only the panel's title, or a config object
31662  */
31663 Roo.GridPanel = function(grid, config){
31664     
31665   
31666     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31667         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31668         
31669     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31670     
31671     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31672     
31673     if(this.toolbar){
31674         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31675     }
31676     // xtype created footer. - not sure if will work as we normally have to render first..
31677     if (this.footer && !this.footer.el && this.footer.xtype) {
31678         
31679         this.footer.container = this.grid.getView().getFooterPanel(true);
31680         this.footer.dataSource = this.grid.dataSource;
31681         this.footer = Roo.factory(this.footer, Roo);
31682         
31683     }
31684     
31685     grid.monitorWindowResize = false; // turn off autosizing
31686     grid.autoHeight = false;
31687     grid.autoWidth = false;
31688     this.grid = grid;
31689     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31690 };
31691
31692 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31693     getId : function(){
31694         return this.grid.id;
31695     },
31696     
31697     /**
31698      * Returns the grid for this panel
31699      * @return {Roo.grid.Grid} 
31700      */
31701     getGrid : function(){
31702         return this.grid;    
31703     },
31704     
31705     setSize : function(width, height){
31706         if(!this.ignoreResize(width, height)){
31707             var grid = this.grid;
31708             var size = this.adjustForComponents(width, height);
31709             grid.getGridEl().setSize(size.width, size.height);
31710             grid.autoSize();
31711         }
31712     },
31713     
31714     beforeSlide : function(){
31715         this.grid.getView().scroller.clip();
31716     },
31717     
31718     afterSlide : function(){
31719         this.grid.getView().scroller.unclip();
31720     },
31721     
31722     destroy : function(){
31723         this.grid.destroy();
31724         delete this.grid;
31725         Roo.GridPanel.superclass.destroy.call(this); 
31726     }
31727 });
31728
31729
31730 /**
31731  * @class Roo.NestedLayoutPanel
31732  * @extends Roo.ContentPanel
31733  * @constructor
31734  * Create a new NestedLayoutPanel.
31735  * 
31736  * 
31737  * @param {Roo.BorderLayout} layout The layout for this panel
31738  * @param {String/Object} config A string to set only the title or a config object
31739  */
31740 Roo.NestedLayoutPanel = function(layout, config)
31741 {
31742     // construct with only one argument..
31743     /* FIXME - implement nicer consturctors
31744     if (layout.layout) {
31745         config = layout;
31746         layout = config.layout;
31747         delete config.layout;
31748     }
31749     if (layout.xtype && !layout.getEl) {
31750         // then layout needs constructing..
31751         layout = Roo.factory(layout, Roo);
31752     }
31753     */
31754     
31755     
31756     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31757     
31758     layout.monitorWindowResize = false; // turn off autosizing
31759     this.layout = layout;
31760     this.layout.getEl().addClass("x-layout-nested-layout");
31761     
31762     
31763     
31764     
31765 };
31766
31767 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31768
31769     setSize : function(width, height){
31770         if(!this.ignoreResize(width, height)){
31771             var size = this.adjustForComponents(width, height);
31772             var el = this.layout.getEl();
31773             el.setSize(size.width, size.height);
31774             var touch = el.dom.offsetWidth;
31775             this.layout.layout();
31776             // ie requires a double layout on the first pass
31777             if(Roo.isIE && !this.initialized){
31778                 this.initialized = true;
31779                 this.layout.layout();
31780             }
31781         }
31782     },
31783     
31784     // activate all subpanels if not currently active..
31785     
31786     setActiveState : function(active){
31787         this.active = active;
31788         if(!active){
31789             this.fireEvent("deactivate", this);
31790             return;
31791         }
31792         
31793         this.fireEvent("activate", this);
31794         // not sure if this should happen before or after..
31795         if (!this.layout) {
31796             return; // should not happen..
31797         }
31798         var reg = false;
31799         for (var r in this.layout.regions) {
31800             reg = this.layout.getRegion(r);
31801             if (reg.getActivePanel()) {
31802                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31803                 reg.setActivePanel(reg.getActivePanel());
31804                 continue;
31805             }
31806             if (!reg.panels.length) {
31807                 continue;
31808             }
31809             reg.showPanel(reg.getPanel(0));
31810         }
31811         
31812         
31813         
31814         
31815     },
31816     
31817     /**
31818      * Returns the nested BorderLayout for this panel
31819      * @return {Roo.BorderLayout} 
31820      */
31821     getLayout : function(){
31822         return this.layout;
31823     },
31824     
31825      /**
31826      * Adds a xtype elements to the layout of the nested panel
31827      * <pre><code>
31828
31829 panel.addxtype({
31830        xtype : 'ContentPanel',
31831        region: 'west',
31832        items: [ .... ]
31833    }
31834 );
31835
31836 panel.addxtype({
31837         xtype : 'NestedLayoutPanel',
31838         region: 'west',
31839         layout: {
31840            center: { },
31841            west: { }   
31842         },
31843         items : [ ... list of content panels or nested layout panels.. ]
31844    }
31845 );
31846 </code></pre>
31847      * @param {Object} cfg Xtype definition of item to add.
31848      */
31849     addxtype : function(cfg) {
31850         return this.layout.addxtype(cfg);
31851     
31852     }
31853 });
31854
31855 Roo.ScrollPanel = function(el, config, content){
31856     config = config || {};
31857     config.fitToFrame = true;
31858     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31859     
31860     this.el.dom.style.overflow = "hidden";
31861     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31862     this.el.removeClass("x-layout-inactive-content");
31863     this.el.on("mousewheel", this.onWheel, this);
31864
31865     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31866     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31867     up.unselectable(); down.unselectable();
31868     up.on("click", this.scrollUp, this);
31869     down.on("click", this.scrollDown, this);
31870     up.addClassOnOver("x-scroller-btn-over");
31871     down.addClassOnOver("x-scroller-btn-over");
31872     up.addClassOnClick("x-scroller-btn-click");
31873     down.addClassOnClick("x-scroller-btn-click");
31874     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31875
31876     this.resizeEl = this.el;
31877     this.el = wrap; this.up = up; this.down = down;
31878 };
31879
31880 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31881     increment : 100,
31882     wheelIncrement : 5,
31883     scrollUp : function(){
31884         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31885     },
31886
31887     scrollDown : function(){
31888         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31889     },
31890
31891     afterScroll : function(){
31892         var el = this.resizeEl;
31893         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31894         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31895         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31896     },
31897
31898     setSize : function(){
31899         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31900         this.afterScroll();
31901     },
31902
31903     onWheel : function(e){
31904         var d = e.getWheelDelta();
31905         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31906         this.afterScroll();
31907         e.stopEvent();
31908     },
31909
31910     setContent : function(content, loadScripts){
31911         this.resizeEl.update(content, loadScripts);
31912     }
31913
31914 });
31915
31916
31917
31918 /**
31919  * @class Roo.TreePanel
31920  * @extends Roo.ContentPanel
31921  * Treepanel component
31922  * 
31923  * @constructor
31924  * Create a new TreePanel. - defaults to fit/scoll contents.
31925  * @param {String/Object} config A string to set only the panel's title, or a config object
31926  */
31927 Roo.TreePanel = function(config){
31928     var el = config.el;
31929     var tree = config.tree;
31930     delete config.tree; 
31931     delete config.el; // hopefull!
31932     
31933     // wrapper for IE7 strict & safari scroll issue
31934     
31935     var treeEl = el.createChild();
31936     config.resizeEl = treeEl;
31937     
31938     
31939     
31940     Roo.TreePanel.superclass.constructor.call(this, el, config);
31941  
31942  
31943     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31944     //console.log(tree);
31945     this.on('activate', function()
31946     {
31947         if (this.tree.rendered) {
31948             return;
31949         }
31950         //console.log('render tree');
31951         this.tree.render();
31952     });
31953     // this should not be needed.. - it's actually the 'el' that resizes?
31954     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31955     
31956     //this.on('resize',  function (cp, w, h) {
31957     //        this.tree.innerCt.setWidth(w);
31958     //        this.tree.innerCt.setHeight(h);
31959     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31960     //});
31961
31962         
31963     
31964 };
31965
31966 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31967     fitToFrame : true,
31968     autoScroll : true,
31969     /*
31970      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31971      */
31972     tree : false
31973
31974 });
31975
31976
31977
31978
31979
31980
31981
31982
31983
31984
31985
31986 /*
31987  * Based on:
31988  * Ext JS Library 1.1.1
31989  * Copyright(c) 2006-2007, Ext JS, LLC.
31990  *
31991  * Originally Released Under LGPL - original licence link has changed is not relivant.
31992  *
31993  * Fork - LGPL
31994  * <script type="text/javascript">
31995  */
31996  
31997
31998 /**
31999  * @class Roo.ReaderLayout
32000  * @extends Roo.BorderLayout
32001  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32002  * center region containing two nested regions (a top one for a list view and one for item preview below),
32003  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32004  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32005  * expedites the setup of the overall layout and regions for this common application style.
32006  * Example:
32007  <pre><code>
32008 var reader = new Roo.ReaderLayout();
32009 var CP = Roo.ContentPanel;  // shortcut for adding
32010
32011 reader.beginUpdate();
32012 reader.add("north", new CP("north", "North"));
32013 reader.add("west", new CP("west", {title: "West"}));
32014 reader.add("east", new CP("east", {title: "East"}));
32015
32016 reader.regions.listView.add(new CP("listView", "List"));
32017 reader.regions.preview.add(new CP("preview", "Preview"));
32018 reader.endUpdate();
32019 </code></pre>
32020 * @constructor
32021 * Create a new ReaderLayout
32022 * @param {Object} config Configuration options
32023 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32024 * document.body if omitted)
32025 */
32026 Roo.ReaderLayout = function(config, renderTo){
32027     var c = config || {size:{}};
32028     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32029         north: c.north !== false ? Roo.apply({
32030             split:false,
32031             initialSize: 32,
32032             titlebar: false
32033         }, c.north) : false,
32034         west: c.west !== false ? Roo.apply({
32035             split:true,
32036             initialSize: 200,
32037             minSize: 175,
32038             maxSize: 400,
32039             titlebar: true,
32040             collapsible: true,
32041             animate: true,
32042             margins:{left:5,right:0,bottom:5,top:5},
32043             cmargins:{left:5,right:5,bottom:5,top:5}
32044         }, c.west) : false,
32045         east: c.east !== false ? Roo.apply({
32046             split:true,
32047             initialSize: 200,
32048             minSize: 175,
32049             maxSize: 400,
32050             titlebar: true,
32051             collapsible: true,
32052             animate: true,
32053             margins:{left:0,right:5,bottom:5,top:5},
32054             cmargins:{left:5,right:5,bottom:5,top:5}
32055         }, c.east) : false,
32056         center: Roo.apply({
32057             tabPosition: 'top',
32058             autoScroll:false,
32059             closeOnTab: true,
32060             titlebar:false,
32061             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32062         }, c.center)
32063     });
32064
32065     this.el.addClass('x-reader');
32066
32067     this.beginUpdate();
32068
32069     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32070         south: c.preview !== false ? Roo.apply({
32071             split:true,
32072             initialSize: 200,
32073             minSize: 100,
32074             autoScroll:true,
32075             collapsible:true,
32076             titlebar: true,
32077             cmargins:{top:5,left:0, right:0, bottom:0}
32078         }, c.preview) : false,
32079         center: Roo.apply({
32080             autoScroll:false,
32081             titlebar:false,
32082             minHeight:200
32083         }, c.listView)
32084     });
32085     this.add('center', new Roo.NestedLayoutPanel(inner,
32086             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32087
32088     this.endUpdate();
32089
32090     this.regions.preview = inner.getRegion('south');
32091     this.regions.listView = inner.getRegion('center');
32092 };
32093
32094 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32095  * Based on:
32096  * Ext JS Library 1.1.1
32097  * Copyright(c) 2006-2007, Ext JS, LLC.
32098  *
32099  * Originally Released Under LGPL - original licence link has changed is not relivant.
32100  *
32101  * Fork - LGPL
32102  * <script type="text/javascript">
32103  */
32104  
32105 /**
32106  * @class Roo.grid.Grid
32107  * @extends Roo.util.Observable
32108  * This class represents the primary interface of a component based grid control.
32109  * <br><br>Usage:<pre><code>
32110  var grid = new Roo.grid.Grid("my-container-id", {
32111      ds: myDataStore,
32112      cm: myColModel,
32113      selModel: mySelectionModel,
32114      autoSizeColumns: true,
32115      monitorWindowResize: false,
32116      trackMouseOver: true
32117  });
32118  // set any options
32119  grid.render();
32120  * </code></pre>
32121  * <b>Common Problems:</b><br/>
32122  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32123  * element will correct this<br/>
32124  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32125  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32126  * are unpredictable.<br/>
32127  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32128  * grid to calculate dimensions/offsets.<br/>
32129   * @constructor
32130  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32131  * The container MUST have some type of size defined for the grid to fill. The container will be
32132  * automatically set to position relative if it isn't already.
32133  * @param {Object} config A config object that sets properties on this grid.
32134  */
32135 Roo.grid.Grid = function(container, config){
32136         // initialize the container
32137         this.container = Roo.get(container);
32138         this.container.update("");
32139         this.container.setStyle("overflow", "hidden");
32140     this.container.addClass('x-grid-container');
32141
32142     this.id = this.container.id;
32143
32144     Roo.apply(this, config);
32145     // check and correct shorthanded configs
32146     if(this.ds){
32147         this.dataSource = this.ds;
32148         delete this.ds;
32149     }
32150     if(this.cm){
32151         this.colModel = this.cm;
32152         delete this.cm;
32153     }
32154     if(this.sm){
32155         this.selModel = this.sm;
32156         delete this.sm;
32157     }
32158
32159     if (this.selModel) {
32160         this.selModel = Roo.factory(this.selModel, Roo.grid);
32161         this.sm = this.selModel;
32162         this.sm.xmodule = this.xmodule || false;
32163     }
32164     if (typeof(this.colModel.config) == 'undefined') {
32165         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32166         this.cm = this.colModel;
32167         this.cm.xmodule = this.xmodule || false;
32168     }
32169     if (this.dataSource) {
32170         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32171         this.ds = this.dataSource;
32172         this.ds.xmodule = this.xmodule || false;
32173          
32174     }
32175     
32176     
32177     
32178     if(this.width){
32179         this.container.setWidth(this.width);
32180     }
32181
32182     if(this.height){
32183         this.container.setHeight(this.height);
32184     }
32185     /** @private */
32186         this.addEvents({
32187         // raw events
32188         /**
32189          * @event click
32190          * The raw click event for the entire grid.
32191          * @param {Roo.EventObject} e
32192          */
32193         "click" : true,
32194         /**
32195          * @event dblclick
32196          * The raw dblclick event for the entire grid.
32197          * @param {Roo.EventObject} e
32198          */
32199         "dblclick" : true,
32200         /**
32201          * @event contextmenu
32202          * The raw contextmenu event for the entire grid.
32203          * @param {Roo.EventObject} e
32204          */
32205         "contextmenu" : true,
32206         /**
32207          * @event mousedown
32208          * The raw mousedown event for the entire grid.
32209          * @param {Roo.EventObject} e
32210          */
32211         "mousedown" : true,
32212         /**
32213          * @event mouseup
32214          * The raw mouseup event for the entire grid.
32215          * @param {Roo.EventObject} e
32216          */
32217         "mouseup" : true,
32218         /**
32219          * @event mouseover
32220          * The raw mouseover event for the entire grid.
32221          * @param {Roo.EventObject} e
32222          */
32223         "mouseover" : true,
32224         /**
32225          * @event mouseout
32226          * The raw mouseout event for the entire grid.
32227          * @param {Roo.EventObject} e
32228          */
32229         "mouseout" : true,
32230         /**
32231          * @event keypress
32232          * The raw keypress event for the entire grid.
32233          * @param {Roo.EventObject} e
32234          */
32235         "keypress" : true,
32236         /**
32237          * @event keydown
32238          * The raw keydown event for the entire grid.
32239          * @param {Roo.EventObject} e
32240          */
32241         "keydown" : true,
32242
32243         // custom events
32244
32245         /**
32246          * @event cellclick
32247          * Fires when a cell is clicked
32248          * @param {Grid} this
32249          * @param {Number} rowIndex
32250          * @param {Number} columnIndex
32251          * @param {Roo.EventObject} e
32252          */
32253         "cellclick" : true,
32254         /**
32255          * @event celldblclick
32256          * Fires when a cell is double clicked
32257          * @param {Grid} this
32258          * @param {Number} rowIndex
32259          * @param {Number} columnIndex
32260          * @param {Roo.EventObject} e
32261          */
32262         "celldblclick" : true,
32263         /**
32264          * @event rowclick
32265          * Fires when a row is clicked
32266          * @param {Grid} this
32267          * @param {Number} rowIndex
32268          * @param {Roo.EventObject} e
32269          */
32270         "rowclick" : true,
32271         /**
32272          * @event rowdblclick
32273          * Fires when a row is double clicked
32274          * @param {Grid} this
32275          * @param {Number} rowIndex
32276          * @param {Roo.EventObject} e
32277          */
32278         "rowdblclick" : true,
32279         /**
32280          * @event headerclick
32281          * Fires when a header is clicked
32282          * @param {Grid} this
32283          * @param {Number} columnIndex
32284          * @param {Roo.EventObject} e
32285          */
32286         "headerclick" : true,
32287         /**
32288          * @event headerdblclick
32289          * Fires when a header cell is double clicked
32290          * @param {Grid} this
32291          * @param {Number} columnIndex
32292          * @param {Roo.EventObject} e
32293          */
32294         "headerdblclick" : true,
32295         /**
32296          * @event rowcontextmenu
32297          * Fires when a row is right clicked
32298          * @param {Grid} this
32299          * @param {Number} rowIndex
32300          * @param {Roo.EventObject} e
32301          */
32302         "rowcontextmenu" : true,
32303         /**
32304          * @event cellcontextmenu
32305          * Fires when a cell is right clicked
32306          * @param {Grid} this
32307          * @param {Number} rowIndex
32308          * @param {Number} cellIndex
32309          * @param {Roo.EventObject} e
32310          */
32311          "cellcontextmenu" : true,
32312         /**
32313          * @event headercontextmenu
32314          * Fires when a header is right clicked
32315          * @param {Grid} this
32316          * @param {Number} columnIndex
32317          * @param {Roo.EventObject} e
32318          */
32319         "headercontextmenu" : true,
32320         /**
32321          * @event bodyscroll
32322          * Fires when the body element is scrolled
32323          * @param {Number} scrollLeft
32324          * @param {Number} scrollTop
32325          */
32326         "bodyscroll" : true,
32327         /**
32328          * @event columnresize
32329          * Fires when the user resizes a column
32330          * @param {Number} columnIndex
32331          * @param {Number} newSize
32332          */
32333         "columnresize" : true,
32334         /**
32335          * @event columnmove
32336          * Fires when the user moves a column
32337          * @param {Number} oldIndex
32338          * @param {Number} newIndex
32339          */
32340         "columnmove" : true,
32341         /**
32342          * @event startdrag
32343          * Fires when row(s) start being dragged
32344          * @param {Grid} this
32345          * @param {Roo.GridDD} dd The drag drop object
32346          * @param {event} e The raw browser event
32347          */
32348         "startdrag" : true,
32349         /**
32350          * @event enddrag
32351          * Fires when a drag operation is complete
32352          * @param {Grid} this
32353          * @param {Roo.GridDD} dd The drag drop object
32354          * @param {event} e The raw browser event
32355          */
32356         "enddrag" : true,
32357         /**
32358          * @event dragdrop
32359          * Fires when dragged row(s) are dropped on a valid DD target
32360          * @param {Grid} this
32361          * @param {Roo.GridDD} dd The drag drop object
32362          * @param {String} targetId The target drag drop object
32363          * @param {event} e The raw browser event
32364          */
32365         "dragdrop" : true,
32366         /**
32367          * @event dragover
32368          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32369          * @param {Grid} this
32370          * @param {Roo.GridDD} dd The drag drop object
32371          * @param {String} targetId The target drag drop object
32372          * @param {event} e The raw browser event
32373          */
32374         "dragover" : true,
32375         /**
32376          * @event dragenter
32377          *  Fires when the dragged row(s) first cross another DD target while being dragged
32378          * @param {Grid} this
32379          * @param {Roo.GridDD} dd The drag drop object
32380          * @param {String} targetId The target drag drop object
32381          * @param {event} e The raw browser event
32382          */
32383         "dragenter" : true,
32384         /**
32385          * @event dragout
32386          * Fires when the dragged row(s) leave another DD target while being dragged
32387          * @param {Grid} this
32388          * @param {Roo.GridDD} dd The drag drop object
32389          * @param {String} targetId The target drag drop object
32390          * @param {event} e The raw browser event
32391          */
32392         "dragout" : true,
32393         /**
32394          * @event rowclass
32395          * Fires when a row is rendered, so you can change add a style to it.
32396          * @param {GridView} gridview   The grid view
32397          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32398          */
32399         'rowclass' : true,
32400
32401         /**
32402          * @event render
32403          * Fires when the grid is rendered
32404          * @param {Grid} grid
32405          */
32406         'render' : true
32407     });
32408
32409     Roo.grid.Grid.superclass.constructor.call(this);
32410 };
32411 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32412     
32413     /**
32414      * @cfg {String} ddGroup - drag drop group.
32415      */
32416       /**
32417      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32418      */
32419
32420     /**
32421      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32422      */
32423     minColumnWidth : 25,
32424
32425     /**
32426      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32427      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32428      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32429      */
32430     autoSizeColumns : false,
32431
32432     /**
32433      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32434      */
32435     autoSizeHeaders : true,
32436
32437     /**
32438      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32439      */
32440     monitorWindowResize : true,
32441
32442     /**
32443      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32444      * rows measured to get a columns size. Default is 0 (all rows).
32445      */
32446     maxRowsToMeasure : 0,
32447
32448     /**
32449      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32450      */
32451     trackMouseOver : true,
32452
32453     /**
32454     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32455     */
32456       /**
32457     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
32458     */
32459     
32460     /**
32461     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32462     */
32463     enableDragDrop : false,
32464     
32465     /**
32466     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32467     */
32468     enableColumnMove : true,
32469     
32470     /**
32471     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32472     */
32473     enableColumnHide : true,
32474     
32475     /**
32476     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32477     */
32478     enableRowHeightSync : false,
32479     
32480     /**
32481     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32482     */
32483     stripeRows : true,
32484     
32485     /**
32486     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32487     */
32488     autoHeight : false,
32489
32490     /**
32491      * @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.
32492      */
32493     autoExpandColumn : false,
32494
32495     /**
32496     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32497     * Default is 50.
32498     */
32499     autoExpandMin : 50,
32500
32501     /**
32502     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32503     */
32504     autoExpandMax : 1000,
32505
32506     /**
32507     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32508     */
32509     view : null,
32510
32511     /**
32512     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32513     */
32514     loadMask : false,
32515     /**
32516     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32517     */
32518     dropTarget: false,
32519     
32520    
32521     
32522     // private
32523     rendered : false,
32524
32525     /**
32526     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32527     * of a fixed width. Default is false.
32528     */
32529     /**
32530     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32531     */
32532     
32533     
32534     /**
32535     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32536     * %0 is replaced with the number of selected rows.
32537     */
32538     ddText : "{0} selected row{1}",
32539     
32540     
32541     /**
32542      * Called once after all setup has been completed and the grid is ready to be rendered.
32543      * @return {Roo.grid.Grid} this
32544      */
32545     render : function()
32546     {
32547         var c = this.container;
32548         // try to detect autoHeight/width mode
32549         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32550             this.autoHeight = true;
32551         }
32552         var view = this.getView();
32553         view.init(this);
32554
32555         c.on("click", this.onClick, this);
32556         c.on("dblclick", this.onDblClick, this);
32557         c.on("contextmenu", this.onContextMenu, this);
32558         c.on("keydown", this.onKeyDown, this);
32559         if (Roo.isTouch) {
32560             c.on("touchstart", this.onTouchStart, this);
32561         }
32562
32563         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32564
32565         this.getSelectionModel().init(this);
32566
32567         view.render();
32568
32569         if(this.loadMask){
32570             this.loadMask = new Roo.LoadMask(this.container,
32571                     Roo.apply({store:this.dataSource}, this.loadMask));
32572         }
32573         
32574         
32575         if (this.toolbar && this.toolbar.xtype) {
32576             this.toolbar.container = this.getView().getHeaderPanel(true);
32577             this.toolbar = new Roo.Toolbar(this.toolbar);
32578         }
32579         if (this.footer && this.footer.xtype) {
32580             this.footer.dataSource = this.getDataSource();
32581             this.footer.container = this.getView().getFooterPanel(true);
32582             this.footer = Roo.factory(this.footer, Roo);
32583         }
32584         if (this.dropTarget && this.dropTarget.xtype) {
32585             delete this.dropTarget.xtype;
32586             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32587         }
32588         
32589         
32590         this.rendered = true;
32591         this.fireEvent('render', this);
32592         return this;
32593     },
32594
32595     /**
32596      * Reconfigures the grid to use a different Store and Column Model.
32597      * The View will be bound to the new objects and refreshed.
32598      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32599      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32600      */
32601     reconfigure : function(dataSource, colModel){
32602         if(this.loadMask){
32603             this.loadMask.destroy();
32604             this.loadMask = new Roo.LoadMask(this.container,
32605                     Roo.apply({store:dataSource}, this.loadMask));
32606         }
32607         this.view.bind(dataSource, colModel);
32608         this.dataSource = dataSource;
32609         this.colModel = colModel;
32610         this.view.refresh(true);
32611     },
32612     /**
32613      * addColumns
32614      * Add's a column, default at the end..
32615      
32616      * @param {int} position to add (default end)
32617      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32618      */
32619     addColumns : function(pos, ar)
32620     {
32621         
32622         for (var i =0;i< ar.length;i++) {
32623             var cfg = ar[i];
32624             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32625             this.cm.lookup[cfg.id] = cfg;
32626         }
32627         
32628         
32629         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32630             pos = this.cm.config.length; //this.cm.config.push(cfg);
32631         } 
32632         pos = Math.max(0,pos);
32633         ar.unshift(0);
32634         ar.unshift(pos);
32635         this.cm.config.splice.apply(this.cm.config, ar);
32636         
32637         
32638         
32639         this.view.generateRules(this.cm);
32640         this.view.refresh(true);
32641         
32642     },
32643     
32644     
32645     
32646     
32647     // private
32648     onKeyDown : function(e){
32649         this.fireEvent("keydown", e);
32650     },
32651
32652     /**
32653      * Destroy this grid.
32654      * @param {Boolean} removeEl True to remove the element
32655      */
32656     destroy : function(removeEl, keepListeners){
32657         if(this.loadMask){
32658             this.loadMask.destroy();
32659         }
32660         var c = this.container;
32661         c.removeAllListeners();
32662         this.view.destroy();
32663         this.colModel.purgeListeners();
32664         if(!keepListeners){
32665             this.purgeListeners();
32666         }
32667         c.update("");
32668         if(removeEl === true){
32669             c.remove();
32670         }
32671     },
32672
32673     // private
32674     processEvent : function(name, e){
32675         // does this fire select???
32676         //Roo.log('grid:processEvent '  + name);
32677         
32678         if (name != 'touchstart' ) {
32679             this.fireEvent(name, e);    
32680         }
32681         
32682         var t = e.getTarget();
32683         var v = this.view;
32684         var header = v.findHeaderIndex(t);
32685         if(header !== false){
32686             var ename = name == 'touchstart' ? 'click' : name;
32687              
32688             this.fireEvent("header" + ename, this, header, e);
32689         }else{
32690             var row = v.findRowIndex(t);
32691             var cell = v.findCellIndex(t);
32692             if (name == 'touchstart') {
32693                 // first touch is always a click.
32694                 // hopefull this happens after selection is updated.?
32695                 name = false;
32696                 
32697                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32698                     var cs = this.selModel.getSelectedCell();
32699                     if (row == cs[0] && cell == cs[1]){
32700                         name = 'dblclick';
32701                     }
32702                 }
32703                 if (typeof(this.selModel.getSelections) != 'undefined') {
32704                     var cs = this.selModel.getSelections();
32705                     var ds = this.dataSource;
32706                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32707                         name = 'dblclick';
32708                     }
32709                 }
32710                 if (!name) {
32711                     return;
32712                 }
32713             }
32714             
32715             
32716             if(row !== false){
32717                 this.fireEvent("row" + name, this, row, e);
32718                 if(cell !== false){
32719                     this.fireEvent("cell" + name, this, row, cell, e);
32720                 }
32721             }
32722         }
32723     },
32724
32725     // private
32726     onClick : function(e){
32727         this.processEvent("click", e);
32728     },
32729    // private
32730     onTouchStart : function(e){
32731         this.processEvent("touchstart", e);
32732     },
32733
32734     // private
32735     onContextMenu : function(e, t){
32736         this.processEvent("contextmenu", e);
32737     },
32738
32739     // private
32740     onDblClick : function(e){
32741         this.processEvent("dblclick", e);
32742     },
32743
32744     // private
32745     walkCells : function(row, col, step, fn, scope){
32746         var cm = this.colModel, clen = cm.getColumnCount();
32747         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32748         if(step < 0){
32749             if(col < 0){
32750                 row--;
32751                 first = false;
32752             }
32753             while(row >= 0){
32754                 if(!first){
32755                     col = clen-1;
32756                 }
32757                 first = false;
32758                 while(col >= 0){
32759                     if(fn.call(scope || this, row, col, cm) === true){
32760                         return [row, col];
32761                     }
32762                     col--;
32763                 }
32764                 row--;
32765             }
32766         } else {
32767             if(col >= clen){
32768                 row++;
32769                 first = false;
32770             }
32771             while(row < rlen){
32772                 if(!first){
32773                     col = 0;
32774                 }
32775                 first = false;
32776                 while(col < clen){
32777                     if(fn.call(scope || this, row, col, cm) === true){
32778                         return [row, col];
32779                     }
32780                     col++;
32781                 }
32782                 row++;
32783             }
32784         }
32785         return null;
32786     },
32787
32788     // private
32789     getSelections : function(){
32790         return this.selModel.getSelections();
32791     },
32792
32793     /**
32794      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32795      * but if manual update is required this method will initiate it.
32796      */
32797     autoSize : function(){
32798         if(this.rendered){
32799             this.view.layout();
32800             if(this.view.adjustForScroll){
32801                 this.view.adjustForScroll();
32802             }
32803         }
32804     },
32805
32806     /**
32807      * Returns the grid's underlying element.
32808      * @return {Element} The element
32809      */
32810     getGridEl : function(){
32811         return this.container;
32812     },
32813
32814     // private for compatibility, overridden by editor grid
32815     stopEditing : function(){},
32816
32817     /**
32818      * Returns the grid's SelectionModel.
32819      * @return {SelectionModel}
32820      */
32821     getSelectionModel : function(){
32822         if(!this.selModel){
32823             this.selModel = new Roo.grid.RowSelectionModel();
32824         }
32825         return this.selModel;
32826     },
32827
32828     /**
32829      * Returns the grid's DataSource.
32830      * @return {DataSource}
32831      */
32832     getDataSource : function(){
32833         return this.dataSource;
32834     },
32835
32836     /**
32837      * Returns the grid's ColumnModel.
32838      * @return {ColumnModel}
32839      */
32840     getColumnModel : function(){
32841         return this.colModel;
32842     },
32843
32844     /**
32845      * Returns the grid's GridView object.
32846      * @return {GridView}
32847      */
32848     getView : function(){
32849         if(!this.view){
32850             this.view = new Roo.grid.GridView(this.viewConfig);
32851             this.relayEvents(this.view, [
32852                 "beforerowremoved", "beforerowsinserted",
32853                 "beforerefresh", "rowremoved",
32854                 "rowsinserted", "rowupdated" ,"refresh"
32855             ]);
32856         }
32857         return this.view;
32858     },
32859     /**
32860      * Called to get grid's drag proxy text, by default returns this.ddText.
32861      * Override this to put something different in the dragged text.
32862      * @return {String}
32863      */
32864     getDragDropText : function(){
32865         var count = this.selModel.getCount();
32866         return String.format(this.ddText, count, count == 1 ? '' : 's');
32867     }
32868 });
32869 /*
32870  * Based on:
32871  * Ext JS Library 1.1.1
32872  * Copyright(c) 2006-2007, Ext JS, LLC.
32873  *
32874  * Originally Released Under LGPL - original licence link has changed is not relivant.
32875  *
32876  * Fork - LGPL
32877  * <script type="text/javascript">
32878  */
32879  
32880 Roo.grid.AbstractGridView = function(){
32881         this.grid = null;
32882         
32883         this.events = {
32884             "beforerowremoved" : true,
32885             "beforerowsinserted" : true,
32886             "beforerefresh" : true,
32887             "rowremoved" : true,
32888             "rowsinserted" : true,
32889             "rowupdated" : true,
32890             "refresh" : true
32891         };
32892     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32893 };
32894
32895 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32896     rowClass : "x-grid-row",
32897     cellClass : "x-grid-cell",
32898     tdClass : "x-grid-td",
32899     hdClass : "x-grid-hd",
32900     splitClass : "x-grid-hd-split",
32901     
32902     init: function(grid){
32903         this.grid = grid;
32904                 var cid = this.grid.getGridEl().id;
32905         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32906         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32907         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32908         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32909         },
32910         
32911     getColumnRenderers : function(){
32912         var renderers = [];
32913         var cm = this.grid.colModel;
32914         var colCount = cm.getColumnCount();
32915         for(var i = 0; i < colCount; i++){
32916             renderers[i] = cm.getRenderer(i);
32917         }
32918         return renderers;
32919     },
32920     
32921     getColumnIds : function(){
32922         var ids = [];
32923         var cm = this.grid.colModel;
32924         var colCount = cm.getColumnCount();
32925         for(var i = 0; i < colCount; i++){
32926             ids[i] = cm.getColumnId(i);
32927         }
32928         return ids;
32929     },
32930     
32931     getDataIndexes : function(){
32932         if(!this.indexMap){
32933             this.indexMap = this.buildIndexMap();
32934         }
32935         return this.indexMap.colToData;
32936     },
32937     
32938     getColumnIndexByDataIndex : function(dataIndex){
32939         if(!this.indexMap){
32940             this.indexMap = this.buildIndexMap();
32941         }
32942         return this.indexMap.dataToCol[dataIndex];
32943     },
32944     
32945     /**
32946      * Set a css style for a column dynamically. 
32947      * @param {Number} colIndex The index of the column
32948      * @param {String} name The css property name
32949      * @param {String} value The css value
32950      */
32951     setCSSStyle : function(colIndex, name, value){
32952         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32953         Roo.util.CSS.updateRule(selector, name, value);
32954     },
32955     
32956     generateRules : function(cm){
32957         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32958         Roo.util.CSS.removeStyleSheet(rulesId);
32959         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32960             var cid = cm.getColumnId(i);
32961             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32962                          this.tdSelector, cid, " {\n}\n",
32963                          this.hdSelector, cid, " {\n}\n",
32964                          this.splitSelector, cid, " {\n}\n");
32965         }
32966         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32967     }
32968 });/*
32969  * Based on:
32970  * Ext JS Library 1.1.1
32971  * Copyright(c) 2006-2007, Ext JS, LLC.
32972  *
32973  * Originally Released Under LGPL - original licence link has changed is not relivant.
32974  *
32975  * Fork - LGPL
32976  * <script type="text/javascript">
32977  */
32978
32979 // private
32980 // This is a support class used internally by the Grid components
32981 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32982     this.grid = grid;
32983     this.view = grid.getView();
32984     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32985     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32986     if(hd2){
32987         this.setHandleElId(Roo.id(hd));
32988         this.setOuterHandleElId(Roo.id(hd2));
32989     }
32990     this.scroll = false;
32991 };
32992 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32993     maxDragWidth: 120,
32994     getDragData : function(e){
32995         var t = Roo.lib.Event.getTarget(e);
32996         var h = this.view.findHeaderCell(t);
32997         if(h){
32998             return {ddel: h.firstChild, header:h};
32999         }
33000         return false;
33001     },
33002
33003     onInitDrag : function(e){
33004         this.view.headersDisabled = true;
33005         var clone = this.dragData.ddel.cloneNode(true);
33006         clone.id = Roo.id();
33007         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33008         this.proxy.update(clone);
33009         return true;
33010     },
33011
33012     afterValidDrop : function(){
33013         var v = this.view;
33014         setTimeout(function(){
33015             v.headersDisabled = false;
33016         }, 50);
33017     },
33018
33019     afterInvalidDrop : function(){
33020         var v = this.view;
33021         setTimeout(function(){
33022             v.headersDisabled = false;
33023         }, 50);
33024     }
33025 });
33026 /*
33027  * Based on:
33028  * Ext JS Library 1.1.1
33029  * Copyright(c) 2006-2007, Ext JS, LLC.
33030  *
33031  * Originally Released Under LGPL - original licence link has changed is not relivant.
33032  *
33033  * Fork - LGPL
33034  * <script type="text/javascript">
33035  */
33036 // private
33037 // This is a support class used internally by the Grid components
33038 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33039     this.grid = grid;
33040     this.view = grid.getView();
33041     // split the proxies so they don't interfere with mouse events
33042     this.proxyTop = Roo.DomHelper.append(document.body, {
33043         cls:"col-move-top", html:"&#160;"
33044     }, true);
33045     this.proxyBottom = Roo.DomHelper.append(document.body, {
33046         cls:"col-move-bottom", html:"&#160;"
33047     }, true);
33048     this.proxyTop.hide = this.proxyBottom.hide = function(){
33049         this.setLeftTop(-100,-100);
33050         this.setStyle("visibility", "hidden");
33051     };
33052     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33053     // temporarily disabled
33054     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33055     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33056 };
33057 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33058     proxyOffsets : [-4, -9],
33059     fly: Roo.Element.fly,
33060
33061     getTargetFromEvent : function(e){
33062         var t = Roo.lib.Event.getTarget(e);
33063         var cindex = this.view.findCellIndex(t);
33064         if(cindex !== false){
33065             return this.view.getHeaderCell(cindex);
33066         }
33067         return null;
33068     },
33069
33070     nextVisible : function(h){
33071         var v = this.view, cm = this.grid.colModel;
33072         h = h.nextSibling;
33073         while(h){
33074             if(!cm.isHidden(v.getCellIndex(h))){
33075                 return h;
33076             }
33077             h = h.nextSibling;
33078         }
33079         return null;
33080     },
33081
33082     prevVisible : function(h){
33083         var v = this.view, cm = this.grid.colModel;
33084         h = h.prevSibling;
33085         while(h){
33086             if(!cm.isHidden(v.getCellIndex(h))){
33087                 return h;
33088             }
33089             h = h.prevSibling;
33090         }
33091         return null;
33092     },
33093
33094     positionIndicator : function(h, n, e){
33095         var x = Roo.lib.Event.getPageX(e);
33096         var r = Roo.lib.Dom.getRegion(n.firstChild);
33097         var px, pt, py = r.top + this.proxyOffsets[1];
33098         if((r.right - x) <= (r.right-r.left)/2){
33099             px = r.right+this.view.borderWidth;
33100             pt = "after";
33101         }else{
33102             px = r.left;
33103             pt = "before";
33104         }
33105         var oldIndex = this.view.getCellIndex(h);
33106         var newIndex = this.view.getCellIndex(n);
33107
33108         if(this.grid.colModel.isFixed(newIndex)){
33109             return false;
33110         }
33111
33112         var locked = this.grid.colModel.isLocked(newIndex);
33113
33114         if(pt == "after"){
33115             newIndex++;
33116         }
33117         if(oldIndex < newIndex){
33118             newIndex--;
33119         }
33120         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33121             return false;
33122         }
33123         px +=  this.proxyOffsets[0];
33124         this.proxyTop.setLeftTop(px, py);
33125         this.proxyTop.show();
33126         if(!this.bottomOffset){
33127             this.bottomOffset = this.view.mainHd.getHeight();
33128         }
33129         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33130         this.proxyBottom.show();
33131         return pt;
33132     },
33133
33134     onNodeEnter : function(n, dd, e, data){
33135         if(data.header != n){
33136             this.positionIndicator(data.header, n, e);
33137         }
33138     },
33139
33140     onNodeOver : function(n, dd, e, data){
33141         var result = false;
33142         if(data.header != n){
33143             result = this.positionIndicator(data.header, n, e);
33144         }
33145         if(!result){
33146             this.proxyTop.hide();
33147             this.proxyBottom.hide();
33148         }
33149         return result ? this.dropAllowed : this.dropNotAllowed;
33150     },
33151
33152     onNodeOut : function(n, dd, e, data){
33153         this.proxyTop.hide();
33154         this.proxyBottom.hide();
33155     },
33156
33157     onNodeDrop : function(n, dd, e, data){
33158         var h = data.header;
33159         if(h != n){
33160             var cm = this.grid.colModel;
33161             var x = Roo.lib.Event.getPageX(e);
33162             var r = Roo.lib.Dom.getRegion(n.firstChild);
33163             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33164             var oldIndex = this.view.getCellIndex(h);
33165             var newIndex = this.view.getCellIndex(n);
33166             var locked = cm.isLocked(newIndex);
33167             if(pt == "after"){
33168                 newIndex++;
33169             }
33170             if(oldIndex < newIndex){
33171                 newIndex--;
33172             }
33173             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33174                 return false;
33175             }
33176             cm.setLocked(oldIndex, locked, true);
33177             cm.moveColumn(oldIndex, newIndex);
33178             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33179             return true;
33180         }
33181         return false;
33182     }
33183 });
33184 /*
33185  * Based on:
33186  * Ext JS Library 1.1.1
33187  * Copyright(c) 2006-2007, Ext JS, LLC.
33188  *
33189  * Originally Released Under LGPL - original licence link has changed is not relivant.
33190  *
33191  * Fork - LGPL
33192  * <script type="text/javascript">
33193  */
33194   
33195 /**
33196  * @class Roo.grid.GridView
33197  * @extends Roo.util.Observable
33198  *
33199  * @constructor
33200  * @param {Object} config
33201  */
33202 Roo.grid.GridView = function(config){
33203     Roo.grid.GridView.superclass.constructor.call(this);
33204     this.el = null;
33205
33206     Roo.apply(this, config);
33207 };
33208
33209 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33210
33211     unselectable :  'unselectable="on"',
33212     unselectableCls :  'x-unselectable',
33213     
33214     
33215     rowClass : "x-grid-row",
33216
33217     cellClass : "x-grid-col",
33218
33219     tdClass : "x-grid-td",
33220
33221     hdClass : "x-grid-hd",
33222
33223     splitClass : "x-grid-split",
33224
33225     sortClasses : ["sort-asc", "sort-desc"],
33226
33227     enableMoveAnim : false,
33228
33229     hlColor: "C3DAF9",
33230
33231     dh : Roo.DomHelper,
33232
33233     fly : Roo.Element.fly,
33234
33235     css : Roo.util.CSS,
33236
33237     borderWidth: 1,
33238
33239     splitOffset: 3,
33240
33241     scrollIncrement : 22,
33242
33243     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33244
33245     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33246
33247     bind : function(ds, cm){
33248         if(this.ds){
33249             this.ds.un("load", this.onLoad, this);
33250             this.ds.un("datachanged", this.onDataChange, this);
33251             this.ds.un("add", this.onAdd, this);
33252             this.ds.un("remove", this.onRemove, this);
33253             this.ds.un("update", this.onUpdate, this);
33254             this.ds.un("clear", this.onClear, this);
33255         }
33256         if(ds){
33257             ds.on("load", this.onLoad, this);
33258             ds.on("datachanged", this.onDataChange, this);
33259             ds.on("add", this.onAdd, this);
33260             ds.on("remove", this.onRemove, this);
33261             ds.on("update", this.onUpdate, this);
33262             ds.on("clear", this.onClear, this);
33263         }
33264         this.ds = ds;
33265
33266         if(this.cm){
33267             this.cm.un("widthchange", this.onColWidthChange, this);
33268             this.cm.un("headerchange", this.onHeaderChange, this);
33269             this.cm.un("hiddenchange", this.onHiddenChange, this);
33270             this.cm.un("columnmoved", this.onColumnMove, this);
33271             this.cm.un("columnlockchange", this.onColumnLock, this);
33272         }
33273         if(cm){
33274             this.generateRules(cm);
33275             cm.on("widthchange", this.onColWidthChange, this);
33276             cm.on("headerchange", this.onHeaderChange, this);
33277             cm.on("hiddenchange", this.onHiddenChange, this);
33278             cm.on("columnmoved", this.onColumnMove, this);
33279             cm.on("columnlockchange", this.onColumnLock, this);
33280         }
33281         this.cm = cm;
33282     },
33283
33284     init: function(grid){
33285         Roo.grid.GridView.superclass.init.call(this, grid);
33286
33287         this.bind(grid.dataSource, grid.colModel);
33288
33289         grid.on("headerclick", this.handleHeaderClick, this);
33290
33291         if(grid.trackMouseOver){
33292             grid.on("mouseover", this.onRowOver, this);
33293             grid.on("mouseout", this.onRowOut, this);
33294         }
33295         grid.cancelTextSelection = function(){};
33296         this.gridId = grid.id;
33297
33298         var tpls = this.templates || {};
33299
33300         if(!tpls.master){
33301             tpls.master = new Roo.Template(
33302                '<div class="x-grid" hidefocus="true">',
33303                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33304                   '<div class="x-grid-topbar"></div>',
33305                   '<div class="x-grid-scroller"><div></div></div>',
33306                   '<div class="x-grid-locked">',
33307                       '<div class="x-grid-header">{lockedHeader}</div>',
33308                       '<div class="x-grid-body">{lockedBody}</div>',
33309                   "</div>",
33310                   '<div class="x-grid-viewport">',
33311                       '<div class="x-grid-header">{header}</div>',
33312                       '<div class="x-grid-body">{body}</div>',
33313                   "</div>",
33314                   '<div class="x-grid-bottombar"></div>',
33315                  
33316                   '<div class="x-grid-resize-proxy">&#160;</div>',
33317                "</div>"
33318             );
33319             tpls.master.disableformats = true;
33320         }
33321
33322         if(!tpls.header){
33323             tpls.header = new Roo.Template(
33324                '<table border="0" cellspacing="0" cellpadding="0">',
33325                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33326                "</table>{splits}"
33327             );
33328             tpls.header.disableformats = true;
33329         }
33330         tpls.header.compile();
33331
33332         if(!tpls.hcell){
33333             tpls.hcell = new Roo.Template(
33334                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33335                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33336                 "</div></td>"
33337              );
33338              tpls.hcell.disableFormats = true;
33339         }
33340         tpls.hcell.compile();
33341
33342         if(!tpls.hsplit){
33343             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33344                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33345             tpls.hsplit.disableFormats = true;
33346         }
33347         tpls.hsplit.compile();
33348
33349         if(!tpls.body){
33350             tpls.body = new Roo.Template(
33351                '<table border="0" cellspacing="0" cellpadding="0">',
33352                "<tbody>{rows}</tbody>",
33353                "</table>"
33354             );
33355             tpls.body.disableFormats = true;
33356         }
33357         tpls.body.compile();
33358
33359         if(!tpls.row){
33360             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33361             tpls.row.disableFormats = true;
33362         }
33363         tpls.row.compile();
33364
33365         if(!tpls.cell){
33366             tpls.cell = new Roo.Template(
33367                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33368                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33369                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33370                 "</td>"
33371             );
33372             tpls.cell.disableFormats = true;
33373         }
33374         tpls.cell.compile();
33375
33376         this.templates = tpls;
33377     },
33378
33379     // remap these for backwards compat
33380     onColWidthChange : function(){
33381         this.updateColumns.apply(this, arguments);
33382     },
33383     onHeaderChange : function(){
33384         this.updateHeaders.apply(this, arguments);
33385     }, 
33386     onHiddenChange : function(){
33387         this.handleHiddenChange.apply(this, arguments);
33388     },
33389     onColumnMove : function(){
33390         this.handleColumnMove.apply(this, arguments);
33391     },
33392     onColumnLock : function(){
33393         this.handleLockChange.apply(this, arguments);
33394     },
33395
33396     onDataChange : function(){
33397         this.refresh();
33398         this.updateHeaderSortState();
33399     },
33400
33401     onClear : function(){
33402         this.refresh();
33403     },
33404
33405     onUpdate : function(ds, record){
33406         this.refreshRow(record);
33407     },
33408
33409     refreshRow : function(record){
33410         var ds = this.ds, index;
33411         if(typeof record == 'number'){
33412             index = record;
33413             record = ds.getAt(index);
33414         }else{
33415             index = ds.indexOf(record);
33416         }
33417         this.insertRows(ds, index, index, true);
33418         this.onRemove(ds, record, index+1, true);
33419         this.syncRowHeights(index, index);
33420         this.layout();
33421         this.fireEvent("rowupdated", this, index, record);
33422     },
33423
33424     onAdd : function(ds, records, index){
33425         this.insertRows(ds, index, index + (records.length-1));
33426     },
33427
33428     onRemove : function(ds, record, index, isUpdate){
33429         if(isUpdate !== true){
33430             this.fireEvent("beforerowremoved", this, index, record);
33431         }
33432         var bt = this.getBodyTable(), lt = this.getLockedTable();
33433         if(bt.rows[index]){
33434             bt.firstChild.removeChild(bt.rows[index]);
33435         }
33436         if(lt.rows[index]){
33437             lt.firstChild.removeChild(lt.rows[index]);
33438         }
33439         if(isUpdate !== true){
33440             this.stripeRows(index);
33441             this.syncRowHeights(index, index);
33442             this.layout();
33443             this.fireEvent("rowremoved", this, index, record);
33444         }
33445     },
33446
33447     onLoad : function(){
33448         this.scrollToTop();
33449     },
33450
33451     /**
33452      * Scrolls the grid to the top
33453      */
33454     scrollToTop : function(){
33455         if(this.scroller){
33456             this.scroller.dom.scrollTop = 0;
33457             this.syncScroll();
33458         }
33459     },
33460
33461     /**
33462      * Gets a panel in the header of the grid that can be used for toolbars etc.
33463      * After modifying the contents of this panel a call to grid.autoSize() may be
33464      * required to register any changes in size.
33465      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33466      * @return Roo.Element
33467      */
33468     getHeaderPanel : function(doShow){
33469         if(doShow){
33470             this.headerPanel.show();
33471         }
33472         return this.headerPanel;
33473     },
33474
33475     /**
33476      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33477      * After modifying the contents of this panel a call to grid.autoSize() may be
33478      * required to register any changes in size.
33479      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33480      * @return Roo.Element
33481      */
33482     getFooterPanel : function(doShow){
33483         if(doShow){
33484             this.footerPanel.show();
33485         }
33486         return this.footerPanel;
33487     },
33488
33489     initElements : function(){
33490         var E = Roo.Element;
33491         var el = this.grid.getGridEl().dom.firstChild;
33492         var cs = el.childNodes;
33493
33494         this.el = new E(el);
33495         
33496          this.focusEl = new E(el.firstChild);
33497         this.focusEl.swallowEvent("click", true);
33498         
33499         this.headerPanel = new E(cs[1]);
33500         this.headerPanel.enableDisplayMode("block");
33501
33502         this.scroller = new E(cs[2]);
33503         this.scrollSizer = new E(this.scroller.dom.firstChild);
33504
33505         this.lockedWrap = new E(cs[3]);
33506         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33507         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33508
33509         this.mainWrap = new E(cs[4]);
33510         this.mainHd = new E(this.mainWrap.dom.firstChild);
33511         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33512
33513         this.footerPanel = new E(cs[5]);
33514         this.footerPanel.enableDisplayMode("block");
33515
33516         this.resizeProxy = new E(cs[6]);
33517
33518         this.headerSelector = String.format(
33519            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33520            this.lockedHd.id, this.mainHd.id
33521         );
33522
33523         this.splitterSelector = String.format(
33524            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33525            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33526         );
33527     },
33528     idToCssName : function(s)
33529     {
33530         return s.replace(/[^a-z0-9]+/ig, '-');
33531     },
33532
33533     getHeaderCell : function(index){
33534         return Roo.DomQuery.select(this.headerSelector)[index];
33535     },
33536
33537     getHeaderCellMeasure : function(index){
33538         return this.getHeaderCell(index).firstChild;
33539     },
33540
33541     getHeaderCellText : function(index){
33542         return this.getHeaderCell(index).firstChild.firstChild;
33543     },
33544
33545     getLockedTable : function(){
33546         return this.lockedBody.dom.firstChild;
33547     },
33548
33549     getBodyTable : function(){
33550         return this.mainBody.dom.firstChild;
33551     },
33552
33553     getLockedRow : function(index){
33554         return this.getLockedTable().rows[index];
33555     },
33556
33557     getRow : function(index){
33558         return this.getBodyTable().rows[index];
33559     },
33560
33561     getRowComposite : function(index){
33562         if(!this.rowEl){
33563             this.rowEl = new Roo.CompositeElementLite();
33564         }
33565         var els = [], lrow, mrow;
33566         if(lrow = this.getLockedRow(index)){
33567             els.push(lrow);
33568         }
33569         if(mrow = this.getRow(index)){
33570             els.push(mrow);
33571         }
33572         this.rowEl.elements = els;
33573         return this.rowEl;
33574     },
33575     /**
33576      * Gets the 'td' of the cell
33577      * 
33578      * @param {Integer} rowIndex row to select
33579      * @param {Integer} colIndex column to select
33580      * 
33581      * @return {Object} 
33582      */
33583     getCell : function(rowIndex, colIndex){
33584         var locked = this.cm.getLockedCount();
33585         var source;
33586         if(colIndex < locked){
33587             source = this.lockedBody.dom.firstChild;
33588         }else{
33589             source = this.mainBody.dom.firstChild;
33590             colIndex -= locked;
33591         }
33592         return source.rows[rowIndex].childNodes[colIndex];
33593     },
33594
33595     getCellText : function(rowIndex, colIndex){
33596         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33597     },
33598
33599     getCellBox : function(cell){
33600         var b = this.fly(cell).getBox();
33601         if(Roo.isOpera){ // opera fails to report the Y
33602             b.y = cell.offsetTop + this.mainBody.getY();
33603         }
33604         return b;
33605     },
33606
33607     getCellIndex : function(cell){
33608         var id = String(cell.className).match(this.cellRE);
33609         if(id){
33610             return parseInt(id[1], 10);
33611         }
33612         return 0;
33613     },
33614
33615     findHeaderIndex : function(n){
33616         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33617         return r ? this.getCellIndex(r) : false;
33618     },
33619
33620     findHeaderCell : function(n){
33621         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33622         return r ? r : false;
33623     },
33624
33625     findRowIndex : function(n){
33626         if(!n){
33627             return false;
33628         }
33629         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33630         return r ? r.rowIndex : false;
33631     },
33632
33633     findCellIndex : function(node){
33634         var stop = this.el.dom;
33635         while(node && node != stop){
33636             if(this.findRE.test(node.className)){
33637                 return this.getCellIndex(node);
33638             }
33639             node = node.parentNode;
33640         }
33641         return false;
33642     },
33643
33644     getColumnId : function(index){
33645         return this.cm.getColumnId(index);
33646     },
33647
33648     getSplitters : function()
33649     {
33650         if(this.splitterSelector){
33651            return Roo.DomQuery.select(this.splitterSelector);
33652         }else{
33653             return null;
33654       }
33655     },
33656
33657     getSplitter : function(index){
33658         return this.getSplitters()[index];
33659     },
33660
33661     onRowOver : function(e, t){
33662         var row;
33663         if((row = this.findRowIndex(t)) !== false){
33664             this.getRowComposite(row).addClass("x-grid-row-over");
33665         }
33666     },
33667
33668     onRowOut : function(e, t){
33669         var row;
33670         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33671             this.getRowComposite(row).removeClass("x-grid-row-over");
33672         }
33673     },
33674
33675     renderHeaders : function(){
33676         var cm = this.cm;
33677         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33678         var cb = [], lb = [], sb = [], lsb = [], p = {};
33679         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33680             p.cellId = "x-grid-hd-0-" + i;
33681             p.splitId = "x-grid-csplit-0-" + i;
33682             p.id = cm.getColumnId(i);
33683             p.value = cm.getColumnHeader(i) || "";
33684             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33685             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33686             if(!cm.isLocked(i)){
33687                 cb[cb.length] = ct.apply(p);
33688                 sb[sb.length] = st.apply(p);
33689             }else{
33690                 lb[lb.length] = ct.apply(p);
33691                 lsb[lsb.length] = st.apply(p);
33692             }
33693         }
33694         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33695                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33696     },
33697
33698     updateHeaders : function(){
33699         var html = this.renderHeaders();
33700         this.lockedHd.update(html[0]);
33701         this.mainHd.update(html[1]);
33702     },
33703
33704     /**
33705      * Focuses the specified row.
33706      * @param {Number} row The row index
33707      */
33708     focusRow : function(row)
33709     {
33710         //Roo.log('GridView.focusRow');
33711         var x = this.scroller.dom.scrollLeft;
33712         this.focusCell(row, 0, false);
33713         this.scroller.dom.scrollLeft = x;
33714     },
33715
33716     /**
33717      * Focuses the specified cell.
33718      * @param {Number} row The row index
33719      * @param {Number} col The column index
33720      * @param {Boolean} hscroll false to disable horizontal scrolling
33721      */
33722     focusCell : function(row, col, hscroll)
33723     {
33724         //Roo.log('GridView.focusCell');
33725         var el = this.ensureVisible(row, col, hscroll);
33726         this.focusEl.alignTo(el, "tl-tl");
33727         if(Roo.isGecko){
33728             this.focusEl.focus();
33729         }else{
33730             this.focusEl.focus.defer(1, this.focusEl);
33731         }
33732     },
33733
33734     /**
33735      * Scrolls the specified cell into view
33736      * @param {Number} row The row index
33737      * @param {Number} col The column index
33738      * @param {Boolean} hscroll false to disable horizontal scrolling
33739      */
33740     ensureVisible : function(row, col, hscroll)
33741     {
33742         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33743         //return null; //disable for testing.
33744         if(typeof row != "number"){
33745             row = row.rowIndex;
33746         }
33747         if(row < 0 && row >= this.ds.getCount()){
33748             return  null;
33749         }
33750         col = (col !== undefined ? col : 0);
33751         var cm = this.grid.colModel;
33752         while(cm.isHidden(col)){
33753             col++;
33754         }
33755
33756         var el = this.getCell(row, col);
33757         if(!el){
33758             return null;
33759         }
33760         var c = this.scroller.dom;
33761
33762         var ctop = parseInt(el.offsetTop, 10);
33763         var cleft = parseInt(el.offsetLeft, 10);
33764         var cbot = ctop + el.offsetHeight;
33765         var cright = cleft + el.offsetWidth;
33766         
33767         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33768         var stop = parseInt(c.scrollTop, 10);
33769         var sleft = parseInt(c.scrollLeft, 10);
33770         var sbot = stop + ch;
33771         var sright = sleft + c.clientWidth;
33772         /*
33773         Roo.log('GridView.ensureVisible:' +
33774                 ' ctop:' + ctop +
33775                 ' c.clientHeight:' + c.clientHeight +
33776                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33777                 ' stop:' + stop +
33778                 ' cbot:' + cbot +
33779                 ' sbot:' + sbot +
33780                 ' ch:' + ch  
33781                 );
33782         */
33783         if(ctop < stop){
33784             c.scrollTop = ctop;
33785             //Roo.log("set scrolltop to ctop DISABLE?");
33786         }else if(cbot > sbot){
33787             //Roo.log("set scrolltop to cbot-ch");
33788             c.scrollTop = cbot-ch;
33789         }
33790         
33791         if(hscroll !== false){
33792             if(cleft < sleft){
33793                 c.scrollLeft = cleft;
33794             }else if(cright > sright){
33795                 c.scrollLeft = cright-c.clientWidth;
33796             }
33797         }
33798          
33799         return el;
33800     },
33801
33802     updateColumns : function(){
33803         this.grid.stopEditing();
33804         var cm = this.grid.colModel, colIds = this.getColumnIds();
33805         //var totalWidth = cm.getTotalWidth();
33806         var pos = 0;
33807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33808             //if(cm.isHidden(i)) continue;
33809             var w = cm.getColumnWidth(i);
33810             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33811             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33812         }
33813         this.updateSplitters();
33814     },
33815
33816     generateRules : function(cm){
33817         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33818         Roo.util.CSS.removeStyleSheet(rulesId);
33819         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33820             var cid = cm.getColumnId(i);
33821             var align = '';
33822             if(cm.config[i].align){
33823                 align = 'text-align:'+cm.config[i].align+';';
33824             }
33825             var hidden = '';
33826             if(cm.isHidden(i)){
33827                 hidden = 'display:none;';
33828             }
33829             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33830             ruleBuf.push(
33831                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33832                     this.hdSelector, cid, " {\n", align, width, "}\n",
33833                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33834                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33835         }
33836         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33837     },
33838
33839     updateSplitters : function(){
33840         var cm = this.cm, s = this.getSplitters();
33841         if(s){ // splitters not created yet
33842             var pos = 0, locked = true;
33843             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33844                 if(cm.isHidden(i)) {
33845                     continue;
33846                 }
33847                 var w = cm.getColumnWidth(i); // make sure it's a number
33848                 if(!cm.isLocked(i) && locked){
33849                     pos = 0;
33850                     locked = false;
33851                 }
33852                 pos += w;
33853                 s[i].style.left = (pos-this.splitOffset) + "px";
33854             }
33855         }
33856     },
33857
33858     handleHiddenChange : function(colModel, colIndex, hidden){
33859         if(hidden){
33860             this.hideColumn(colIndex);
33861         }else{
33862             this.unhideColumn(colIndex);
33863         }
33864     },
33865
33866     hideColumn : function(colIndex){
33867         var cid = this.getColumnId(colIndex);
33868         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33869         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33870         if(Roo.isSafari){
33871             this.updateHeaders();
33872         }
33873         this.updateSplitters();
33874         this.layout();
33875     },
33876
33877     unhideColumn : function(colIndex){
33878         var cid = this.getColumnId(colIndex);
33879         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33880         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33881
33882         if(Roo.isSafari){
33883             this.updateHeaders();
33884         }
33885         this.updateSplitters();
33886         this.layout();
33887     },
33888
33889     insertRows : function(dm, firstRow, lastRow, isUpdate){
33890         if(firstRow == 0 && lastRow == dm.getCount()-1){
33891             this.refresh();
33892         }else{
33893             if(!isUpdate){
33894                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33895             }
33896             var s = this.getScrollState();
33897             var markup = this.renderRows(firstRow, lastRow);
33898             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33899             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33900             this.restoreScroll(s);
33901             if(!isUpdate){
33902                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33903                 this.syncRowHeights(firstRow, lastRow);
33904                 this.stripeRows(firstRow);
33905                 this.layout();
33906             }
33907         }
33908     },
33909
33910     bufferRows : function(markup, target, index){
33911         var before = null, trows = target.rows, tbody = target.tBodies[0];
33912         if(index < trows.length){
33913             before = trows[index];
33914         }
33915         var b = document.createElement("div");
33916         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33917         var rows = b.firstChild.rows;
33918         for(var i = 0, len = rows.length; i < len; i++){
33919             if(before){
33920                 tbody.insertBefore(rows[0], before);
33921             }else{
33922                 tbody.appendChild(rows[0]);
33923             }
33924         }
33925         b.innerHTML = "";
33926         b = null;
33927     },
33928
33929     deleteRows : function(dm, firstRow, lastRow){
33930         if(dm.getRowCount()<1){
33931             this.fireEvent("beforerefresh", this);
33932             this.mainBody.update("");
33933             this.lockedBody.update("");
33934             this.fireEvent("refresh", this);
33935         }else{
33936             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33937             var bt = this.getBodyTable();
33938             var tbody = bt.firstChild;
33939             var rows = bt.rows;
33940             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33941                 tbody.removeChild(rows[firstRow]);
33942             }
33943             this.stripeRows(firstRow);
33944             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33945         }
33946     },
33947
33948     updateRows : function(dataSource, firstRow, lastRow){
33949         var s = this.getScrollState();
33950         this.refresh();
33951         this.restoreScroll(s);
33952     },
33953
33954     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33955         if(!noRefresh){
33956            this.refresh();
33957         }
33958         this.updateHeaderSortState();
33959     },
33960
33961     getScrollState : function(){
33962         
33963         var sb = this.scroller.dom;
33964         return {left: sb.scrollLeft, top: sb.scrollTop};
33965     },
33966
33967     stripeRows : function(startRow){
33968         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33969             return;
33970         }
33971         startRow = startRow || 0;
33972         var rows = this.getBodyTable().rows;
33973         var lrows = this.getLockedTable().rows;
33974         var cls = ' x-grid-row-alt ';
33975         for(var i = startRow, len = rows.length; i < len; i++){
33976             var row = rows[i], lrow = lrows[i];
33977             var isAlt = ((i+1) % 2 == 0);
33978             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33979             if(isAlt == hasAlt){
33980                 continue;
33981             }
33982             if(isAlt){
33983                 row.className += " x-grid-row-alt";
33984             }else{
33985                 row.className = row.className.replace("x-grid-row-alt", "");
33986             }
33987             if(lrow){
33988                 lrow.className = row.className;
33989             }
33990         }
33991     },
33992
33993     restoreScroll : function(state){
33994         //Roo.log('GridView.restoreScroll');
33995         var sb = this.scroller.dom;
33996         sb.scrollLeft = state.left;
33997         sb.scrollTop = state.top;
33998         this.syncScroll();
33999     },
34000
34001     syncScroll : function(){
34002         //Roo.log('GridView.syncScroll');
34003         var sb = this.scroller.dom;
34004         var sh = this.mainHd.dom;
34005         var bs = this.mainBody.dom;
34006         var lv = this.lockedBody.dom;
34007         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34008         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34009     },
34010
34011     handleScroll : function(e){
34012         this.syncScroll();
34013         var sb = this.scroller.dom;
34014         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34015         e.stopEvent();
34016     },
34017
34018     handleWheel : function(e){
34019         var d = e.getWheelDelta();
34020         this.scroller.dom.scrollTop -= d*22;
34021         // set this here to prevent jumpy scrolling on large tables
34022         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34023         e.stopEvent();
34024     },
34025
34026     renderRows : function(startRow, endRow){
34027         // pull in all the crap needed to render rows
34028         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34029         var colCount = cm.getColumnCount();
34030
34031         if(ds.getCount() < 1){
34032             return ["", ""];
34033         }
34034
34035         // build a map for all the columns
34036         var cs = [];
34037         for(var i = 0; i < colCount; i++){
34038             var name = cm.getDataIndex(i);
34039             cs[i] = {
34040                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34041                 renderer : cm.getRenderer(i),
34042                 id : cm.getColumnId(i),
34043                 locked : cm.isLocked(i),
34044                 has_editor : cm.isCellEditable(i)
34045             };
34046         }
34047
34048         startRow = startRow || 0;
34049         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34050
34051         // records to render
34052         var rs = ds.getRange(startRow, endRow);
34053
34054         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34055     },
34056
34057     // As much as I hate to duplicate code, this was branched because FireFox really hates
34058     // [].join("") on strings. The performance difference was substantial enough to
34059     // branch this function
34060     doRender : Roo.isGecko ?
34061             function(cs, rs, ds, startRow, colCount, stripe){
34062                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34063                 // buffers
34064                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34065                 
34066                 var hasListener = this.grid.hasListener('rowclass');
34067                 var rowcfg = {};
34068                 for(var j = 0, len = rs.length; j < len; j++){
34069                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34070                     for(var i = 0; i < colCount; i++){
34071                         c = cs[i];
34072                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34073                         p.id = c.id;
34074                         p.css = p.attr = "";
34075                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34076                         if(p.value == undefined || p.value === "") {
34077                             p.value = "&#160;";
34078                         }
34079                         if(c.has_editor){
34080                             p.css += ' x-grid-editable-cell';
34081                         }
34082                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34083                             p.css +=  ' x-grid-dirty-cell';
34084                         }
34085                         var markup = ct.apply(p);
34086                         if(!c.locked){
34087                             cb+= markup;
34088                         }else{
34089                             lcb+= markup;
34090                         }
34091                     }
34092                     var alt = [];
34093                     if(stripe && ((rowIndex+1) % 2 == 0)){
34094                         alt.push("x-grid-row-alt")
34095                     }
34096                     if(r.dirty){
34097                         alt.push(  " x-grid-dirty-row");
34098                     }
34099                     rp.cells = lcb;
34100                     if(this.getRowClass){
34101                         alt.push(this.getRowClass(r, rowIndex));
34102                     }
34103                     if (hasListener) {
34104                         rowcfg = {
34105                              
34106                             record: r,
34107                             rowIndex : rowIndex,
34108                             rowClass : ''
34109                         };
34110                         this.grid.fireEvent('rowclass', this, rowcfg);
34111                         alt.push(rowcfg.rowClass);
34112                     }
34113                     rp.alt = alt.join(" ");
34114                     lbuf+= rt.apply(rp);
34115                     rp.cells = cb;
34116                     buf+=  rt.apply(rp);
34117                 }
34118                 return [lbuf, buf];
34119             } :
34120             function(cs, rs, ds, startRow, colCount, stripe){
34121                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34122                 // buffers
34123                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34124                 var hasListener = this.grid.hasListener('rowclass');
34125  
34126                 var rowcfg = {};
34127                 for(var j = 0, len = rs.length; j < len; j++){
34128                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34129                     for(var i = 0; i < colCount; i++){
34130                         c = cs[i];
34131                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34132                         p.id = c.id;
34133                         p.css = p.attr = "";
34134                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34135                         if(p.value == undefined || p.value === "") {
34136                             p.value = "&#160;";
34137                         }
34138                         //Roo.log(c);
34139                          if(c.has_editor){
34140                             p.css += ' x-grid-editable-cell';
34141                         }
34142                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34143                             p.css += ' x-grid-dirty-cell' 
34144                         }
34145                         
34146                         var markup = ct.apply(p);
34147                         if(!c.locked){
34148                             cb[cb.length] = markup;
34149                         }else{
34150                             lcb[lcb.length] = markup;
34151                         }
34152                     }
34153                     var alt = [];
34154                     if(stripe && ((rowIndex+1) % 2 == 0)){
34155                         alt.push( "x-grid-row-alt");
34156                     }
34157                     if(r.dirty){
34158                         alt.push(" x-grid-dirty-row");
34159                     }
34160                     rp.cells = lcb;
34161                     if(this.getRowClass){
34162                         alt.push( this.getRowClass(r, rowIndex));
34163                     }
34164                     if (hasListener) {
34165                         rowcfg = {
34166                              
34167                             record: r,
34168                             rowIndex : rowIndex,
34169                             rowClass : ''
34170                         };
34171                         this.grid.fireEvent('rowclass', this, rowcfg);
34172                         alt.push(rowcfg.rowClass);
34173                     }
34174                     
34175                     rp.alt = alt.join(" ");
34176                     rp.cells = lcb.join("");
34177                     lbuf[lbuf.length] = rt.apply(rp);
34178                     rp.cells = cb.join("");
34179                     buf[buf.length] =  rt.apply(rp);
34180                 }
34181                 return [lbuf.join(""), buf.join("")];
34182             },
34183
34184     renderBody : function(){
34185         var markup = this.renderRows();
34186         var bt = this.templates.body;
34187         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34188     },
34189
34190     /**
34191      * Refreshes the grid
34192      * @param {Boolean} headersToo
34193      */
34194     refresh : function(headersToo){
34195         this.fireEvent("beforerefresh", this);
34196         this.grid.stopEditing();
34197         var result = this.renderBody();
34198         this.lockedBody.update(result[0]);
34199         this.mainBody.update(result[1]);
34200         if(headersToo === true){
34201             this.updateHeaders();
34202             this.updateColumns();
34203             this.updateSplitters();
34204             this.updateHeaderSortState();
34205         }
34206         this.syncRowHeights();
34207         this.layout();
34208         this.fireEvent("refresh", this);
34209     },
34210
34211     handleColumnMove : function(cm, oldIndex, newIndex){
34212         this.indexMap = null;
34213         var s = this.getScrollState();
34214         this.refresh(true);
34215         this.restoreScroll(s);
34216         this.afterMove(newIndex);
34217     },
34218
34219     afterMove : function(colIndex){
34220         if(this.enableMoveAnim && Roo.enableFx){
34221             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34222         }
34223         // if multisort - fix sortOrder, and reload..
34224         if (this.grid.dataSource.multiSort) {
34225             // the we can call sort again..
34226             var dm = this.grid.dataSource;
34227             var cm = this.grid.colModel;
34228             var so = [];
34229             for(var i = 0; i < cm.config.length; i++ ) {
34230                 
34231                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34232                     continue; // dont' bother, it's not in sort list or being set.
34233                 }
34234                 
34235                 so.push(cm.config[i].dataIndex);
34236             };
34237             dm.sortOrder = so;
34238             dm.load(dm.lastOptions);
34239             
34240             
34241         }
34242         
34243     },
34244
34245     updateCell : function(dm, rowIndex, dataIndex){
34246         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34247         if(typeof colIndex == "undefined"){ // not present in grid
34248             return;
34249         }
34250         var cm = this.grid.colModel;
34251         var cell = this.getCell(rowIndex, colIndex);
34252         var cellText = this.getCellText(rowIndex, colIndex);
34253
34254         var p = {
34255             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34256             id : cm.getColumnId(colIndex),
34257             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34258         };
34259         var renderer = cm.getRenderer(colIndex);
34260         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34261         if(typeof val == "undefined" || val === "") {
34262             val = "&#160;";
34263         }
34264         cellText.innerHTML = val;
34265         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34266         this.syncRowHeights(rowIndex, rowIndex);
34267     },
34268
34269     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34270         var maxWidth = 0;
34271         if(this.grid.autoSizeHeaders){
34272             var h = this.getHeaderCellMeasure(colIndex);
34273             maxWidth = Math.max(maxWidth, h.scrollWidth);
34274         }
34275         var tb, index;
34276         if(this.cm.isLocked(colIndex)){
34277             tb = this.getLockedTable();
34278             index = colIndex;
34279         }else{
34280             tb = this.getBodyTable();
34281             index = colIndex - this.cm.getLockedCount();
34282         }
34283         if(tb && tb.rows){
34284             var rows = tb.rows;
34285             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34286             for(var i = 0; i < stopIndex; i++){
34287                 var cell = rows[i].childNodes[index].firstChild;
34288                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34289             }
34290         }
34291         return maxWidth + /*margin for error in IE*/ 5;
34292     },
34293     /**
34294      * Autofit a column to its content.
34295      * @param {Number} colIndex
34296      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34297      */
34298      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34299          if(this.cm.isHidden(colIndex)){
34300              return; // can't calc a hidden column
34301          }
34302         if(forceMinSize){
34303             var cid = this.cm.getColumnId(colIndex);
34304             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34305            if(this.grid.autoSizeHeaders){
34306                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34307            }
34308         }
34309         var newWidth = this.calcColumnWidth(colIndex);
34310         this.cm.setColumnWidth(colIndex,
34311             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34312         if(!suppressEvent){
34313             this.grid.fireEvent("columnresize", colIndex, newWidth);
34314         }
34315     },
34316
34317     /**
34318      * Autofits all columns to their content and then expands to fit any extra space in the grid
34319      */
34320      autoSizeColumns : function(){
34321         var cm = this.grid.colModel;
34322         var colCount = cm.getColumnCount();
34323         for(var i = 0; i < colCount; i++){
34324             this.autoSizeColumn(i, true, true);
34325         }
34326         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34327             this.fitColumns();
34328         }else{
34329             this.updateColumns();
34330             this.layout();
34331         }
34332     },
34333
34334     /**
34335      * Autofits all columns to the grid's width proportionate with their current size
34336      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34337      */
34338     fitColumns : function(reserveScrollSpace){
34339         var cm = this.grid.colModel;
34340         var colCount = cm.getColumnCount();
34341         var cols = [];
34342         var width = 0;
34343         var i, w;
34344         for (i = 0; i < colCount; i++){
34345             if(!cm.isHidden(i) && !cm.isFixed(i)){
34346                 w = cm.getColumnWidth(i);
34347                 cols.push(i);
34348                 cols.push(w);
34349                 width += w;
34350             }
34351         }
34352         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34353         if(reserveScrollSpace){
34354             avail -= 17;
34355         }
34356         var frac = (avail - cm.getTotalWidth())/width;
34357         while (cols.length){
34358             w = cols.pop();
34359             i = cols.pop();
34360             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34361         }
34362         this.updateColumns();
34363         this.layout();
34364     },
34365
34366     onRowSelect : function(rowIndex){
34367         var row = this.getRowComposite(rowIndex);
34368         row.addClass("x-grid-row-selected");
34369     },
34370
34371     onRowDeselect : function(rowIndex){
34372         var row = this.getRowComposite(rowIndex);
34373         row.removeClass("x-grid-row-selected");
34374     },
34375
34376     onCellSelect : function(row, col){
34377         var cell = this.getCell(row, col);
34378         if(cell){
34379             Roo.fly(cell).addClass("x-grid-cell-selected");
34380         }
34381     },
34382
34383     onCellDeselect : function(row, col){
34384         var cell = this.getCell(row, col);
34385         if(cell){
34386             Roo.fly(cell).removeClass("x-grid-cell-selected");
34387         }
34388     },
34389
34390     updateHeaderSortState : function(){
34391         
34392         // sort state can be single { field: xxx, direction : yyy}
34393         // or   { xxx=>ASC , yyy : DESC ..... }
34394         
34395         var mstate = {};
34396         if (!this.ds.multiSort) { 
34397             var state = this.ds.getSortState();
34398             if(!state){
34399                 return;
34400             }
34401             mstate[state.field] = state.direction;
34402             // FIXME... - this is not used here.. but might be elsewhere..
34403             this.sortState = state;
34404             
34405         } else {
34406             mstate = this.ds.sortToggle;
34407         }
34408         //remove existing sort classes..
34409         
34410         var sc = this.sortClasses;
34411         var hds = this.el.select(this.headerSelector).removeClass(sc);
34412         
34413         for(var f in mstate) {
34414         
34415             var sortColumn = this.cm.findColumnIndex(f);
34416             
34417             if(sortColumn != -1){
34418                 var sortDir = mstate[f];        
34419                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34420             }
34421         }
34422         
34423          
34424         
34425     },
34426
34427
34428     handleHeaderClick : function(g, index,e){
34429         
34430         Roo.log("header click");
34431         
34432         if (Roo.isTouch) {
34433             // touch events on header are handled by context
34434             this.handleHdCtx(g,index,e);
34435             return;
34436         }
34437         
34438         
34439         if(this.headersDisabled){
34440             return;
34441         }
34442         var dm = g.dataSource, cm = g.colModel;
34443         if(!cm.isSortable(index)){
34444             return;
34445         }
34446         g.stopEditing();
34447         
34448         if (dm.multiSort) {
34449             // update the sortOrder
34450             var so = [];
34451             for(var i = 0; i < cm.config.length; i++ ) {
34452                 
34453                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34454                     continue; // dont' bother, it's not in sort list or being set.
34455                 }
34456                 
34457                 so.push(cm.config[i].dataIndex);
34458             };
34459             dm.sortOrder = so;
34460         }
34461         
34462         
34463         dm.sort(cm.getDataIndex(index));
34464     },
34465
34466
34467     destroy : function(){
34468         if(this.colMenu){
34469             this.colMenu.removeAll();
34470             Roo.menu.MenuMgr.unregister(this.colMenu);
34471             this.colMenu.getEl().remove();
34472             delete this.colMenu;
34473         }
34474         if(this.hmenu){
34475             this.hmenu.removeAll();
34476             Roo.menu.MenuMgr.unregister(this.hmenu);
34477             this.hmenu.getEl().remove();
34478             delete this.hmenu;
34479         }
34480         if(this.grid.enableColumnMove){
34481             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34482             if(dds){
34483                 for(var dd in dds){
34484                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34485                         var elid = dds[dd].dragElId;
34486                         dds[dd].unreg();
34487                         Roo.get(elid).remove();
34488                     } else if(dds[dd].config.isTarget){
34489                         dds[dd].proxyTop.remove();
34490                         dds[dd].proxyBottom.remove();
34491                         dds[dd].unreg();
34492                     }
34493                     if(Roo.dd.DDM.locationCache[dd]){
34494                         delete Roo.dd.DDM.locationCache[dd];
34495                     }
34496                 }
34497                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34498             }
34499         }
34500         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34501         this.bind(null, null);
34502         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34503     },
34504
34505     handleLockChange : function(){
34506         this.refresh(true);
34507     },
34508
34509     onDenyColumnLock : function(){
34510
34511     },
34512
34513     onDenyColumnHide : function(){
34514
34515     },
34516
34517     handleHdMenuClick : function(item){
34518         var index = this.hdCtxIndex;
34519         var cm = this.cm, ds = this.ds;
34520         switch(item.id){
34521             case "asc":
34522                 ds.sort(cm.getDataIndex(index), "ASC");
34523                 break;
34524             case "desc":
34525                 ds.sort(cm.getDataIndex(index), "DESC");
34526                 break;
34527             case "lock":
34528                 var lc = cm.getLockedCount();
34529                 if(cm.getColumnCount(true) <= lc+1){
34530                     this.onDenyColumnLock();
34531                     return;
34532                 }
34533                 if(lc != index){
34534                     cm.setLocked(index, true, true);
34535                     cm.moveColumn(index, lc);
34536                     this.grid.fireEvent("columnmove", index, lc);
34537                 }else{
34538                     cm.setLocked(index, true);
34539                 }
34540             break;
34541             case "unlock":
34542                 var lc = cm.getLockedCount();
34543                 if((lc-1) != index){
34544                     cm.setLocked(index, false, true);
34545                     cm.moveColumn(index, lc-1);
34546                     this.grid.fireEvent("columnmove", index, lc-1);
34547                 }else{
34548                     cm.setLocked(index, false);
34549                 }
34550             break;
34551             case 'wider': // used to expand cols on touch..
34552             case 'narrow':
34553                 var cw = cm.getColumnWidth(index);
34554                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34555                 cw = Math.max(0, cw);
34556                 cw = Math.min(cw,4000);
34557                 cm.setColumnWidth(index, cw);
34558                 break;
34559                 
34560             default:
34561                 index = cm.getIndexById(item.id.substr(4));
34562                 if(index != -1){
34563                     if(item.checked && cm.getColumnCount(true) <= 1){
34564                         this.onDenyColumnHide();
34565                         return false;
34566                     }
34567                     cm.setHidden(index, item.checked);
34568                 }
34569         }
34570         return true;
34571     },
34572
34573     beforeColMenuShow : function(){
34574         var cm = this.cm,  colCount = cm.getColumnCount();
34575         this.colMenu.removeAll();
34576         for(var i = 0; i < colCount; i++){
34577             this.colMenu.add(new Roo.menu.CheckItem({
34578                 id: "col-"+cm.getColumnId(i),
34579                 text: cm.getColumnHeader(i),
34580                 checked: !cm.isHidden(i),
34581                 hideOnClick:false
34582             }));
34583         }
34584     },
34585
34586     handleHdCtx : function(g, index, e){
34587         e.stopEvent();
34588         var hd = this.getHeaderCell(index);
34589         this.hdCtxIndex = index;
34590         var ms = this.hmenu.items, cm = this.cm;
34591         ms.get("asc").setDisabled(!cm.isSortable(index));
34592         ms.get("desc").setDisabled(!cm.isSortable(index));
34593         if(this.grid.enableColLock !== false){
34594             ms.get("lock").setDisabled(cm.isLocked(index));
34595             ms.get("unlock").setDisabled(!cm.isLocked(index));
34596         }
34597         this.hmenu.show(hd, "tl-bl");
34598     },
34599
34600     handleHdOver : function(e){
34601         var hd = this.findHeaderCell(e.getTarget());
34602         if(hd && !this.headersDisabled){
34603             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34604                this.fly(hd).addClass("x-grid-hd-over");
34605             }
34606         }
34607     },
34608
34609     handleHdOut : function(e){
34610         var hd = this.findHeaderCell(e.getTarget());
34611         if(hd){
34612             this.fly(hd).removeClass("x-grid-hd-over");
34613         }
34614     },
34615
34616     handleSplitDblClick : function(e, t){
34617         var i = this.getCellIndex(t);
34618         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34619             this.autoSizeColumn(i, true);
34620             this.layout();
34621         }
34622     },
34623
34624     render : function(){
34625
34626         var cm = this.cm;
34627         var colCount = cm.getColumnCount();
34628
34629         if(this.grid.monitorWindowResize === true){
34630             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34631         }
34632         var header = this.renderHeaders();
34633         var body = this.templates.body.apply({rows:""});
34634         var html = this.templates.master.apply({
34635             lockedBody: body,
34636             body: body,
34637             lockedHeader: header[0],
34638             header: header[1]
34639         });
34640
34641         //this.updateColumns();
34642
34643         this.grid.getGridEl().dom.innerHTML = html;
34644
34645         this.initElements();
34646         
34647         // a kludge to fix the random scolling effect in webkit
34648         this.el.on("scroll", function() {
34649             this.el.dom.scrollTop=0; // hopefully not recursive..
34650         },this);
34651
34652         this.scroller.on("scroll", this.handleScroll, this);
34653         this.lockedBody.on("mousewheel", this.handleWheel, this);
34654         this.mainBody.on("mousewheel", this.handleWheel, this);
34655
34656         this.mainHd.on("mouseover", this.handleHdOver, this);
34657         this.mainHd.on("mouseout", this.handleHdOut, this);
34658         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34659                 {delegate: "."+this.splitClass});
34660
34661         this.lockedHd.on("mouseover", this.handleHdOver, this);
34662         this.lockedHd.on("mouseout", this.handleHdOut, this);
34663         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34664                 {delegate: "."+this.splitClass});
34665
34666         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34667             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34668         }
34669
34670         this.updateSplitters();
34671
34672         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34673             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34674             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34675         }
34676
34677         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34678             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34679             this.hmenu.add(
34680                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34681                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34682             );
34683             if(this.grid.enableColLock !== false){
34684                 this.hmenu.add('-',
34685                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34686                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34687                 );
34688             }
34689             if (Roo.isTouch) {
34690                  this.hmenu.add('-',
34691                     {id:"wider", text: this.columnsWiderText},
34692                     {id:"narrow", text: this.columnsNarrowText }
34693                 );
34694                 
34695                  
34696             }
34697             
34698             if(this.grid.enableColumnHide !== false){
34699
34700                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34701                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34702                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34703
34704                 this.hmenu.add('-',
34705                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34706                 );
34707             }
34708             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34709
34710             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34711         }
34712
34713         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34714             this.dd = new Roo.grid.GridDragZone(this.grid, {
34715                 ddGroup : this.grid.ddGroup || 'GridDD'
34716             });
34717             
34718         }
34719
34720         /*
34721         for(var i = 0; i < colCount; i++){
34722             if(cm.isHidden(i)){
34723                 this.hideColumn(i);
34724             }
34725             if(cm.config[i].align){
34726                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34727                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34728             }
34729         }*/
34730         
34731         this.updateHeaderSortState();
34732
34733         this.beforeInitialResize();
34734         this.layout(true);
34735
34736         // two part rendering gives faster view to the user
34737         this.renderPhase2.defer(1, this);
34738     },
34739
34740     renderPhase2 : function(){
34741         // render the rows now
34742         this.refresh();
34743         if(this.grid.autoSizeColumns){
34744             this.autoSizeColumns();
34745         }
34746     },
34747
34748     beforeInitialResize : function(){
34749
34750     },
34751
34752     onColumnSplitterMoved : function(i, w){
34753         this.userResized = true;
34754         var cm = this.grid.colModel;
34755         cm.setColumnWidth(i, w, true);
34756         var cid = cm.getColumnId(i);
34757         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34758         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34759         this.updateSplitters();
34760         this.layout();
34761         this.grid.fireEvent("columnresize", i, w);
34762     },
34763
34764     syncRowHeights : function(startIndex, endIndex){
34765         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34766             startIndex = startIndex || 0;
34767             var mrows = this.getBodyTable().rows;
34768             var lrows = this.getLockedTable().rows;
34769             var len = mrows.length-1;
34770             endIndex = Math.min(endIndex || len, len);
34771             for(var i = startIndex; i <= endIndex; i++){
34772                 var m = mrows[i], l = lrows[i];
34773                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34774                 m.style.height = l.style.height = h + "px";
34775             }
34776         }
34777     },
34778
34779     layout : function(initialRender, is2ndPass)
34780     {
34781         var g = this.grid;
34782         var auto = g.autoHeight;
34783         var scrollOffset = 16;
34784         var c = g.getGridEl(), cm = this.cm,
34785                 expandCol = g.autoExpandColumn,
34786                 gv = this;
34787         //c.beginMeasure();
34788
34789         if(!c.dom.offsetWidth){ // display:none?
34790             if(initialRender){
34791                 this.lockedWrap.show();
34792                 this.mainWrap.show();
34793             }
34794             return;
34795         }
34796
34797         var hasLock = this.cm.isLocked(0);
34798
34799         var tbh = this.headerPanel.getHeight();
34800         var bbh = this.footerPanel.getHeight();
34801
34802         if(auto){
34803             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34804             var newHeight = ch + c.getBorderWidth("tb");
34805             if(g.maxHeight){
34806                 newHeight = Math.min(g.maxHeight, newHeight);
34807             }
34808             c.setHeight(newHeight);
34809         }
34810
34811         if(g.autoWidth){
34812             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34813         }
34814
34815         var s = this.scroller;
34816
34817         var csize = c.getSize(true);
34818
34819         this.el.setSize(csize.width, csize.height);
34820
34821         this.headerPanel.setWidth(csize.width);
34822         this.footerPanel.setWidth(csize.width);
34823
34824         var hdHeight = this.mainHd.getHeight();
34825         var vw = csize.width;
34826         var vh = csize.height - (tbh + bbh);
34827
34828         s.setSize(vw, vh);
34829
34830         var bt = this.getBodyTable();
34831         
34832         if(cm.getLockedCount() == cm.config.length){
34833             bt = this.getLockedTable();
34834         }
34835         
34836         var ltWidth = hasLock ?
34837                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34838
34839         var scrollHeight = bt.offsetHeight;
34840         var scrollWidth = ltWidth + bt.offsetWidth;
34841         var vscroll = false, hscroll = false;
34842
34843         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34844
34845         var lw = this.lockedWrap, mw = this.mainWrap;
34846         var lb = this.lockedBody, mb = this.mainBody;
34847
34848         setTimeout(function(){
34849             var t = s.dom.offsetTop;
34850             var w = s.dom.clientWidth,
34851                 h = s.dom.clientHeight;
34852
34853             lw.setTop(t);
34854             lw.setSize(ltWidth, h);
34855
34856             mw.setLeftTop(ltWidth, t);
34857             mw.setSize(w-ltWidth, h);
34858
34859             lb.setHeight(h-hdHeight);
34860             mb.setHeight(h-hdHeight);
34861
34862             if(is2ndPass !== true && !gv.userResized && expandCol){
34863                 // high speed resize without full column calculation
34864                 
34865                 var ci = cm.getIndexById(expandCol);
34866                 if (ci < 0) {
34867                     ci = cm.findColumnIndex(expandCol);
34868                 }
34869                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34870                 var expandId = cm.getColumnId(ci);
34871                 var  tw = cm.getTotalWidth(false);
34872                 var currentWidth = cm.getColumnWidth(ci);
34873                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34874                 if(currentWidth != cw){
34875                     cm.setColumnWidth(ci, cw, true);
34876                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34877                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34878                     gv.updateSplitters();
34879                     gv.layout(false, true);
34880                 }
34881             }
34882
34883             if(initialRender){
34884                 lw.show();
34885                 mw.show();
34886             }
34887             //c.endMeasure();
34888         }, 10);
34889     },
34890
34891     onWindowResize : function(){
34892         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34893             return;
34894         }
34895         this.layout();
34896     },
34897
34898     appendFooter : function(parentEl){
34899         return null;
34900     },
34901
34902     sortAscText : "Sort Ascending",
34903     sortDescText : "Sort Descending",
34904     lockText : "Lock Column",
34905     unlockText : "Unlock Column",
34906     columnsText : "Columns",
34907  
34908     columnsWiderText : "Wider",
34909     columnsNarrowText : "Thinner"
34910 });
34911
34912
34913 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34914     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34915     this.proxy.el.addClass('x-grid3-col-dd');
34916 };
34917
34918 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34919     handleMouseDown : function(e){
34920
34921     },
34922
34923     callHandleMouseDown : function(e){
34924         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34925     }
34926 });
34927 /*
34928  * Based on:
34929  * Ext JS Library 1.1.1
34930  * Copyright(c) 2006-2007, Ext JS, LLC.
34931  *
34932  * Originally Released Under LGPL - original licence link has changed is not relivant.
34933  *
34934  * Fork - LGPL
34935  * <script type="text/javascript">
34936  */
34937  /**
34938  * @extends Roo.dd.DDProxy
34939  * @class Roo.grid.SplitDragZone
34940  * Support for Column Header resizing
34941  * @constructor
34942  * @param {Object} config
34943  */
34944 // private
34945 // This is a support class used internally by the Grid components
34946 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34947     this.grid = grid;
34948     this.view = grid.getView();
34949     this.proxy = this.view.resizeProxy;
34950     Roo.grid.SplitDragZone.superclass.constructor.call(
34951         this,
34952         hd, // ID
34953         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
34954         {  // CONFIG
34955             dragElId : Roo.id(this.proxy.dom),
34956             resizeFrame:false
34957         }
34958     );
34959     
34960     this.setHandleElId(Roo.id(hd));
34961     if (hd2 !== false) {
34962         this.setOuterHandleElId(Roo.id(hd2));
34963     }
34964     
34965     this.scroll = false;
34966 };
34967 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34968     fly: Roo.Element.fly,
34969
34970     b4StartDrag : function(x, y){
34971         this.view.headersDisabled = true;
34972         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
34973                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
34974         );
34975         this.proxy.setHeight(h);
34976         
34977         // for old system colWidth really stored the actual width?
34978         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
34979         // which in reality did not work.. - it worked only for fixed sizes
34980         // for resizable we need to use actual sizes.
34981         var w = this.cm.getColumnWidth(this.cellIndex);
34982         if (!this.view.mainWrap) {
34983             // bootstrap.
34984             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
34985         }
34986         
34987         
34988         
34989         // this was w-this.grid.minColumnWidth;
34990         // doesnt really make sense? - w = thie curren width or the rendered one?
34991         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34992         this.resetConstraints();
34993         this.setXConstraint(minw, 1000);
34994         this.setYConstraint(0, 0);
34995         this.minX = x - minw;
34996         this.maxX = x + 1000;
34997         this.startPos = x;
34998         if (!this.view.mainWrap) { // this is Bootstrap code..
34999             this.getDragEl().style.display='block';
35000         }
35001         
35002         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35003     },
35004
35005
35006     handleMouseDown : function(e){
35007         ev = Roo.EventObject.setEvent(e);
35008         var t = this.fly(ev.getTarget());
35009         if(t.hasClass("x-grid-split")){
35010             this.cellIndex = this.view.getCellIndex(t.dom);
35011             this.split = t.dom;
35012             this.cm = this.grid.colModel;
35013             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35014                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35015             }
35016         }
35017     },
35018
35019     endDrag : function(e){
35020         this.view.headersDisabled = false;
35021         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35022         var diff = endX - this.startPos;
35023         // 
35024         var w = this.cm.getColumnWidth(this.cellIndex);
35025         if (!this.view.mainWrap) {
35026             w = 0;
35027         }
35028         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35029     },
35030
35031     autoOffset : function(){
35032         this.setDelta(0,0);
35033     }
35034 });/*
35035  * Based on:
35036  * Ext JS Library 1.1.1
35037  * Copyright(c) 2006-2007, Ext JS, LLC.
35038  *
35039  * Originally Released Under LGPL - original licence link has changed is not relivant.
35040  *
35041  * Fork - LGPL
35042  * <script type="text/javascript">
35043  */
35044  
35045 // private
35046 // This is a support class used internally by the Grid components
35047 Roo.grid.GridDragZone = function(grid, config){
35048     this.view = grid.getView();
35049     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35050     if(this.view.lockedBody){
35051         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35052         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35053     }
35054     this.scroll = false;
35055     this.grid = grid;
35056     this.ddel = document.createElement('div');
35057     this.ddel.className = 'x-grid-dd-wrap';
35058 };
35059
35060 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35061     ddGroup : "GridDD",
35062
35063     getDragData : function(e){
35064         var t = Roo.lib.Event.getTarget(e);
35065         var rowIndex = this.view.findRowIndex(t);
35066         var sm = this.grid.selModel;
35067             
35068         //Roo.log(rowIndex);
35069         
35070         if (sm.getSelectedCell) {
35071             // cell selection..
35072             if (!sm.getSelectedCell()) {
35073                 return false;
35074             }
35075             if (rowIndex != sm.getSelectedCell()[0]) {
35076                 return false;
35077             }
35078         
35079         }
35080         if (sm.getSelections && sm.getSelections().length < 1) {
35081             return false;
35082         }
35083         
35084         
35085         // before it used to all dragging of unseleted... - now we dont do that.
35086         if(rowIndex !== false){
35087             
35088             // if editorgrid.. 
35089             
35090             
35091             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35092                
35093             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35094               //  
35095             //}
35096             if (e.hasModifier()){
35097                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35098             }
35099             
35100             Roo.log("getDragData");
35101             
35102             return {
35103                 grid: this.grid,
35104                 ddel: this.ddel,
35105                 rowIndex: rowIndex,
35106                 selections: sm.getSelections ? sm.getSelections() : (
35107                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35108             };
35109         }
35110         return false;
35111     },
35112     
35113     
35114     onInitDrag : function(e){
35115         var data = this.dragData;
35116         this.ddel.innerHTML = this.grid.getDragDropText();
35117         this.proxy.update(this.ddel);
35118         // fire start drag?
35119     },
35120
35121     afterRepair : function(){
35122         this.dragging = false;
35123     },
35124
35125     getRepairXY : function(e, data){
35126         return false;
35127     },
35128
35129     onEndDrag : function(data, e){
35130         // fire end drag?
35131     },
35132
35133     onValidDrop : function(dd, e, id){
35134         // fire drag drop?
35135         this.hideProxy();
35136     },
35137
35138     beforeInvalidDrop : function(e, id){
35139
35140     }
35141 });/*
35142  * Based on:
35143  * Ext JS Library 1.1.1
35144  * Copyright(c) 2006-2007, Ext JS, LLC.
35145  *
35146  * Originally Released Under LGPL - original licence link has changed is not relivant.
35147  *
35148  * Fork - LGPL
35149  * <script type="text/javascript">
35150  */
35151  
35152
35153 /**
35154  * @class Roo.grid.ColumnModel
35155  * @extends Roo.util.Observable
35156  * This is the default implementation of a ColumnModel used by the Grid. It defines
35157  * the columns in the grid.
35158  * <br>Usage:<br>
35159  <pre><code>
35160  var colModel = new Roo.grid.ColumnModel([
35161         {header: "Ticker", width: 60, sortable: true, locked: true},
35162         {header: "Company Name", width: 150, sortable: true},
35163         {header: "Market Cap.", width: 100, sortable: true},
35164         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35165         {header: "Employees", width: 100, sortable: true, resizable: false}
35166  ]);
35167  </code></pre>
35168  * <p>
35169  
35170  * The config options listed for this class are options which may appear in each
35171  * individual column definition.
35172  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35173  * @constructor
35174  * @param {Object} config An Array of column config objects. See this class's
35175  * config objects for details.
35176 */
35177 Roo.grid.ColumnModel = function(config){
35178         /**
35179      * The config passed into the constructor
35180      */
35181     this.config = []; //config;
35182     this.lookup = {};
35183
35184     // if no id, create one
35185     // if the column does not have a dataIndex mapping,
35186     // map it to the order it is in the config
35187     for(var i = 0, len = config.length; i < len; i++){
35188         this.addColumn(config[i]);
35189         
35190     }
35191
35192     /**
35193      * The width of columns which have no width specified (defaults to 100)
35194      * @type Number
35195      */
35196     this.defaultWidth = 100;
35197
35198     /**
35199      * Default sortable of columns which have no sortable specified (defaults to false)
35200      * @type Boolean
35201      */
35202     this.defaultSortable = false;
35203
35204     this.addEvents({
35205         /**
35206              * @event widthchange
35207              * Fires when the width of a column changes.
35208              * @param {ColumnModel} this
35209              * @param {Number} columnIndex The column index
35210              * @param {Number} newWidth The new width
35211              */
35212             "widthchange": true,
35213         /**
35214              * @event headerchange
35215              * Fires when the text of a header changes.
35216              * @param {ColumnModel} this
35217              * @param {Number} columnIndex The column index
35218              * @param {Number} newText The new header text
35219              */
35220             "headerchange": true,
35221         /**
35222              * @event hiddenchange
35223              * Fires when a column is hidden or "unhidden".
35224              * @param {ColumnModel} this
35225              * @param {Number} columnIndex The column index
35226              * @param {Boolean} hidden true if hidden, false otherwise
35227              */
35228             "hiddenchange": true,
35229             /**
35230          * @event columnmoved
35231          * Fires when a column is moved.
35232          * @param {ColumnModel} this
35233          * @param {Number} oldIndex
35234          * @param {Number} newIndex
35235          */
35236         "columnmoved" : true,
35237         /**
35238          * @event columlockchange
35239          * Fires when a column's locked state is changed
35240          * @param {ColumnModel} this
35241          * @param {Number} colIndex
35242          * @param {Boolean} locked true if locked
35243          */
35244         "columnlockchange" : true
35245     });
35246     Roo.grid.ColumnModel.superclass.constructor.call(this);
35247 };
35248 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35249     /**
35250      * @cfg {String} header The header text to display in the Grid view.
35251      */
35252         /**
35253      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35254      */
35255         /**
35256      * @cfg {String} smHeader Header at Bootsrap Small width
35257      */
35258         /**
35259      * @cfg {String} mdHeader Header at Bootsrap Medium width
35260      */
35261         /**
35262      * @cfg {String} lgHeader Header at Bootsrap Large width
35263      */
35264         /**
35265      * @cfg {String} xlHeader Header at Bootsrap extra Large width
35266      */
35267     /**
35268      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35269      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35270      * specified, the column's index is used as an index into the Record's data Array.
35271      */
35272     /**
35273      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35274      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35275      */
35276     /**
35277      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35278      * Defaults to the value of the {@link #defaultSortable} property.
35279      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35280      */
35281     /**
35282      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35283      */
35284     /**
35285      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35286      */
35287     /**
35288      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35289      */
35290     /**
35291      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35292      */
35293     /**
35294      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35295      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35296      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35297      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35298      */
35299        /**
35300      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35301      */
35302     /**
35303      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35304      */
35305     /**
35306      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35307      */
35308     /**
35309      * @cfg {String} cursor (Optional)
35310      */
35311     /**
35312      * @cfg {String} tooltip (Optional)
35313      */
35314     /**
35315      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35316      */
35317     /**
35318      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35319      */
35320     /**
35321      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35322      */
35323     /**
35324      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35325      */
35326         /**
35327      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35328      */
35329     /**
35330      * Returns the id of the column at the specified index.
35331      * @param {Number} index The column index
35332      * @return {String} the id
35333      */
35334     getColumnId : function(index){
35335         return this.config[index].id;
35336     },
35337
35338     /**
35339      * Returns the column for a specified id.
35340      * @param {String} id The column id
35341      * @return {Object} the column
35342      */
35343     getColumnById : function(id){
35344         return this.lookup[id];
35345     },
35346
35347     
35348     /**
35349      * Returns the column Object for a specified dataIndex.
35350      * @param {String} dataIndex The column dataIndex
35351      * @return {Object|Boolean} the column or false if not found
35352      */
35353     getColumnByDataIndex: function(dataIndex){
35354         var index = this.findColumnIndex(dataIndex);
35355         return index > -1 ? this.config[index] : false;
35356     },
35357     
35358     /**
35359      * Returns the index for a specified column id.
35360      * @param {String} id The column id
35361      * @return {Number} the index, or -1 if not found
35362      */
35363     getIndexById : function(id){
35364         for(var i = 0, len = this.config.length; i < len; i++){
35365             if(this.config[i].id == id){
35366                 return i;
35367             }
35368         }
35369         return -1;
35370     },
35371     
35372     /**
35373      * Returns the index for a specified column dataIndex.
35374      * @param {String} dataIndex The column dataIndex
35375      * @return {Number} the index, or -1 if not found
35376      */
35377     
35378     findColumnIndex : function(dataIndex){
35379         for(var i = 0, len = this.config.length; i < len; i++){
35380             if(this.config[i].dataIndex == dataIndex){
35381                 return i;
35382             }
35383         }
35384         return -1;
35385     },
35386     
35387     
35388     moveColumn : function(oldIndex, newIndex){
35389         var c = this.config[oldIndex];
35390         this.config.splice(oldIndex, 1);
35391         this.config.splice(newIndex, 0, c);
35392         this.dataMap = null;
35393         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35394     },
35395
35396     isLocked : function(colIndex){
35397         return this.config[colIndex].locked === true;
35398     },
35399
35400     setLocked : function(colIndex, value, suppressEvent){
35401         if(this.isLocked(colIndex) == value){
35402             return;
35403         }
35404         this.config[colIndex].locked = value;
35405         if(!suppressEvent){
35406             this.fireEvent("columnlockchange", this, colIndex, value);
35407         }
35408     },
35409
35410     getTotalLockedWidth : function(){
35411         var totalWidth = 0;
35412         for(var i = 0; i < this.config.length; i++){
35413             if(this.isLocked(i) && !this.isHidden(i)){
35414                 this.totalWidth += this.getColumnWidth(i);
35415             }
35416         }
35417         return totalWidth;
35418     },
35419
35420     getLockedCount : function(){
35421         for(var i = 0, len = this.config.length; i < len; i++){
35422             if(!this.isLocked(i)){
35423                 return i;
35424             }
35425         }
35426         
35427         return this.config.length;
35428     },
35429
35430     /**
35431      * Returns the number of columns.
35432      * @return {Number}
35433      */
35434     getColumnCount : function(visibleOnly){
35435         if(visibleOnly === true){
35436             var c = 0;
35437             for(var i = 0, len = this.config.length; i < len; i++){
35438                 if(!this.isHidden(i)){
35439                     c++;
35440                 }
35441             }
35442             return c;
35443         }
35444         return this.config.length;
35445     },
35446
35447     /**
35448      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35449      * @param {Function} fn
35450      * @param {Object} scope (optional)
35451      * @return {Array} result
35452      */
35453     getColumnsBy : function(fn, scope){
35454         var r = [];
35455         for(var i = 0, len = this.config.length; i < len; i++){
35456             var c = this.config[i];
35457             if(fn.call(scope||this, c, i) === true){
35458                 r[r.length] = c;
35459             }
35460         }
35461         return r;
35462     },
35463
35464     /**
35465      * Returns true if the specified column is sortable.
35466      * @param {Number} col The column index
35467      * @return {Boolean}
35468      */
35469     isSortable : function(col){
35470         if(typeof this.config[col].sortable == "undefined"){
35471             return this.defaultSortable;
35472         }
35473         return this.config[col].sortable;
35474     },
35475
35476     /**
35477      * Returns the rendering (formatting) function defined for the column.
35478      * @param {Number} col The column index.
35479      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35480      */
35481     getRenderer : function(col){
35482         if(!this.config[col].renderer){
35483             return Roo.grid.ColumnModel.defaultRenderer;
35484         }
35485         return this.config[col].renderer;
35486     },
35487
35488     /**
35489      * Sets the rendering (formatting) function for a column.
35490      * @param {Number} col The column index
35491      * @param {Function} fn The function to use to process the cell's raw data
35492      * to return HTML markup for the grid view. The render function is called with
35493      * the following parameters:<ul>
35494      * <li>Data value.</li>
35495      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35496      * <li>css A CSS style string to apply to the table cell.</li>
35497      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35498      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35499      * <li>Row index</li>
35500      * <li>Column index</li>
35501      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35502      */
35503     setRenderer : function(col, fn){
35504         this.config[col].renderer = fn;
35505     },
35506
35507     /**
35508      * Returns the width for the specified column.
35509      * @param {Number} col The column index
35510      * @param (optional) {String} gridSize bootstrap width size.
35511      * @return {Number}
35512      */
35513     getColumnWidth : function(col, gridSize)
35514         {
35515                 var cfg = this.config[col];
35516                 
35517                 if (typeof(gridSize) == 'undefined') {
35518                         return cfg.width * 1 || this.defaultWidth;
35519                 }
35520                 if (gridSize === false) { // if we set it..
35521                         return cfg.width || false;
35522                 }
35523                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35524                 
35525                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35526                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35527                                 continue;
35528                         }
35529                         return cfg[ sizes[i] ];
35530                 }
35531                 return 1;
35532                 
35533     },
35534
35535     /**
35536      * Sets the width for a column.
35537      * @param {Number} col The column index
35538      * @param {Number} width The new width
35539      */
35540     setColumnWidth : function(col, width, suppressEvent){
35541         this.config[col].width = width;
35542         this.totalWidth = null;
35543         if(!suppressEvent){
35544              this.fireEvent("widthchange", this, col, width);
35545         }
35546     },
35547
35548     /**
35549      * Returns the total width of all columns.
35550      * @param {Boolean} includeHidden True to include hidden column widths
35551      * @return {Number}
35552      */
35553     getTotalWidth : function(includeHidden){
35554         if(!this.totalWidth){
35555             this.totalWidth = 0;
35556             for(var i = 0, len = this.config.length; i < len; i++){
35557                 if(includeHidden || !this.isHidden(i)){
35558                     this.totalWidth += this.getColumnWidth(i);
35559                 }
35560             }
35561         }
35562         return this.totalWidth;
35563     },
35564
35565     /**
35566      * Returns the header for the specified column.
35567      * @param {Number} col The column index
35568      * @return {String}
35569      */
35570     getColumnHeader : function(col){
35571         return this.config[col].header;
35572     },
35573
35574     /**
35575      * Sets the header for a column.
35576      * @param {Number} col The column index
35577      * @param {String} header The new header
35578      */
35579     setColumnHeader : function(col, header){
35580         this.config[col].header = header;
35581         this.fireEvent("headerchange", this, col, header);
35582     },
35583
35584     /**
35585      * Returns the tooltip for the specified column.
35586      * @param {Number} col The column index
35587      * @return {String}
35588      */
35589     getColumnTooltip : function(col){
35590             return this.config[col].tooltip;
35591     },
35592     /**
35593      * Sets the tooltip for a column.
35594      * @param {Number} col The column index
35595      * @param {String} tooltip The new tooltip
35596      */
35597     setColumnTooltip : function(col, tooltip){
35598             this.config[col].tooltip = tooltip;
35599     },
35600
35601     /**
35602      * Returns the dataIndex for the specified column.
35603      * @param {Number} col The column index
35604      * @return {Number}
35605      */
35606     getDataIndex : function(col){
35607         return this.config[col].dataIndex;
35608     },
35609
35610     /**
35611      * Sets the dataIndex for a column.
35612      * @param {Number} col The column index
35613      * @param {Number} dataIndex The new dataIndex
35614      */
35615     setDataIndex : function(col, dataIndex){
35616         this.config[col].dataIndex = dataIndex;
35617     },
35618
35619     
35620     
35621     /**
35622      * Returns true if the cell is editable.
35623      * @param {Number} colIndex The column index
35624      * @param {Number} rowIndex The row index - this is nto actually used..?
35625      * @return {Boolean}
35626      */
35627     isCellEditable : function(colIndex, rowIndex){
35628         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35629     },
35630
35631     /**
35632      * Returns the editor defined for the cell/column.
35633      * return false or null to disable editing.
35634      * @param {Number} colIndex The column index
35635      * @param {Number} rowIndex The row index
35636      * @return {Object}
35637      */
35638     getCellEditor : function(colIndex, rowIndex){
35639         return this.config[colIndex].editor;
35640     },
35641
35642     /**
35643      * Sets if a column is editable.
35644      * @param {Number} col The column index
35645      * @param {Boolean} editable True if the column is editable
35646      */
35647     setEditable : function(col, editable){
35648         this.config[col].editable = editable;
35649     },
35650
35651
35652     /**
35653      * Returns true if the column is hidden.
35654      * @param {Number} colIndex The column index
35655      * @return {Boolean}
35656      */
35657     isHidden : function(colIndex){
35658         return this.config[colIndex].hidden;
35659     },
35660
35661
35662     /**
35663      * Returns true if the column width cannot be changed
35664      */
35665     isFixed : function(colIndex){
35666         return this.config[colIndex].fixed;
35667     },
35668
35669     /**
35670      * Returns true if the column can be resized
35671      * @return {Boolean}
35672      */
35673     isResizable : function(colIndex){
35674         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35675     },
35676     /**
35677      * Sets if a column is hidden.
35678      * @param {Number} colIndex The column index
35679      * @param {Boolean} hidden True if the column is hidden
35680      */
35681     setHidden : function(colIndex, hidden){
35682         this.config[colIndex].hidden = hidden;
35683         this.totalWidth = null;
35684         this.fireEvent("hiddenchange", this, colIndex, hidden);
35685     },
35686
35687     /**
35688      * Sets the editor for a column.
35689      * @param {Number} col The column index
35690      * @param {Object} editor The editor object
35691      */
35692     setEditor : function(col, editor){
35693         this.config[col].editor = editor;
35694     },
35695     /**
35696      * Add a column (experimental...) - defaults to adding to the end..
35697      * @param {Object} config 
35698     */
35699     addColumn : function(c)
35700     {
35701     
35702         var i = this.config.length;
35703         this.config[i] = c;
35704         
35705         if(typeof c.dataIndex == "undefined"){
35706             c.dataIndex = i;
35707         }
35708         if(typeof c.renderer == "string"){
35709             c.renderer = Roo.util.Format[c.renderer];
35710         }
35711         if(typeof c.id == "undefined"){
35712             c.id = Roo.id();
35713         }
35714         if(c.editor && c.editor.xtype){
35715             c.editor  = Roo.factory(c.editor, Roo.grid);
35716         }
35717         if(c.editor && c.editor.isFormField){
35718             c.editor = new Roo.grid.GridEditor(c.editor);
35719         }
35720         this.lookup[c.id] = c;
35721     }
35722     
35723 });
35724
35725 Roo.grid.ColumnModel.defaultRenderer = function(value)
35726 {
35727     if(typeof value == "object") {
35728         return value;
35729     }
35730         if(typeof value == "string" && value.length < 1){
35731             return "&#160;";
35732         }
35733     
35734         return String.format("{0}", value);
35735 };
35736
35737 // Alias for backwards compatibility
35738 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35739 /*
35740  * Based on:
35741  * Ext JS Library 1.1.1
35742  * Copyright(c) 2006-2007, Ext JS, LLC.
35743  *
35744  * Originally Released Under LGPL - original licence link has changed is not relivant.
35745  *
35746  * Fork - LGPL
35747  * <script type="text/javascript">
35748  */
35749
35750 /**
35751  * @class Roo.grid.AbstractSelectionModel
35752  * @extends Roo.util.Observable
35753  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35754  * implemented by descendant classes.  This class should not be directly instantiated.
35755  * @constructor
35756  */
35757 Roo.grid.AbstractSelectionModel = function(){
35758     this.locked = false;
35759     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35760 };
35761
35762 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35763     /** @ignore Called by the grid automatically. Do not call directly. */
35764     init : function(grid){
35765         this.grid = grid;
35766         this.initEvents();
35767     },
35768
35769     /**
35770      * Locks the selections.
35771      */
35772     lock : function(){
35773         this.locked = true;
35774     },
35775
35776     /**
35777      * Unlocks the selections.
35778      */
35779     unlock : function(){
35780         this.locked = false;
35781     },
35782
35783     /**
35784      * Returns true if the selections are locked.
35785      * @return {Boolean}
35786      */
35787     isLocked : function(){
35788         return this.locked;
35789     }
35790 });/*
35791  * Based on:
35792  * Ext JS Library 1.1.1
35793  * Copyright(c) 2006-2007, Ext JS, LLC.
35794  *
35795  * Originally Released Under LGPL - original licence link has changed is not relivant.
35796  *
35797  * Fork - LGPL
35798  * <script type="text/javascript">
35799  */
35800 /**
35801  * @extends Roo.grid.AbstractSelectionModel
35802  * @class Roo.grid.RowSelectionModel
35803  * The default SelectionModel used by {@link Roo.grid.Grid}.
35804  * It supports multiple selections and keyboard selection/navigation. 
35805  * @constructor
35806  * @param {Object} config
35807  */
35808 Roo.grid.RowSelectionModel = function(config){
35809     Roo.apply(this, config);
35810     this.selections = new Roo.util.MixedCollection(false, function(o){
35811         return o.id;
35812     });
35813
35814     this.last = false;
35815     this.lastActive = false;
35816
35817     this.addEvents({
35818         /**
35819         * @event selectionchange
35820         * Fires when the selection changes
35821         * @param {SelectionModel} this
35822         */
35823        "selectionchange" : true,
35824        /**
35825         * @event afterselectionchange
35826         * Fires after the selection changes (eg. by key press or clicking)
35827         * @param {SelectionModel} this
35828         */
35829        "afterselectionchange" : true,
35830        /**
35831         * @event beforerowselect
35832         * Fires when a row is selected being selected, return false to cancel.
35833         * @param {SelectionModel} this
35834         * @param {Number} rowIndex The selected index
35835         * @param {Boolean} keepExisting False if other selections will be cleared
35836         */
35837        "beforerowselect" : true,
35838        /**
35839         * @event rowselect
35840         * Fires when a row is selected.
35841         * @param {SelectionModel} this
35842         * @param {Number} rowIndex The selected index
35843         * @param {Roo.data.Record} r The record
35844         */
35845        "rowselect" : true,
35846        /**
35847         * @event rowdeselect
35848         * Fires when a row is deselected.
35849         * @param {SelectionModel} this
35850         * @param {Number} rowIndex The selected index
35851         */
35852         "rowdeselect" : true
35853     });
35854     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35855     this.locked = false;
35856 };
35857
35858 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35859     /**
35860      * @cfg {Boolean} singleSelect
35861      * True to allow selection of only one row at a time (defaults to false)
35862      */
35863     singleSelect : false,
35864
35865     // private
35866     initEvents : function(){
35867
35868         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35869             this.grid.on("mousedown", this.handleMouseDown, this);
35870         }else{ // allow click to work like normal
35871             this.grid.on("rowclick", this.handleDragableRowClick, this);
35872         }
35873         // bootstrap does not have a view..
35874         var view = this.grid.view ? this.grid.view : this.grid;
35875         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35876             "up" : function(e){
35877                 if(!e.shiftKey){
35878                     this.selectPrevious(e.shiftKey);
35879                 }else if(this.last !== false && this.lastActive !== false){
35880                     var last = this.last;
35881                     this.selectRange(this.last,  this.lastActive-1);
35882                     view.focusRow(this.lastActive);
35883                     if(last !== false){
35884                         this.last = last;
35885                     }
35886                 }else{
35887                     this.selectFirstRow();
35888                 }
35889                 this.fireEvent("afterselectionchange", this);
35890             },
35891             "down" : function(e){
35892                 if(!e.shiftKey){
35893                     this.selectNext(e.shiftKey);
35894                 }else if(this.last !== false && this.lastActive !== false){
35895                     var last = this.last;
35896                     this.selectRange(this.last,  this.lastActive+1);
35897                     view.focusRow(this.lastActive);
35898                     if(last !== false){
35899                         this.last = last;
35900                     }
35901                 }else{
35902                     this.selectFirstRow();
35903                 }
35904                 this.fireEvent("afterselectionchange", this);
35905             },
35906             scope: this
35907         });
35908
35909          
35910         view.on("refresh", this.onRefresh, this);
35911         view.on("rowupdated", this.onRowUpdated, this);
35912         view.on("rowremoved", this.onRemove, this);
35913     },
35914
35915     // private
35916     onRefresh : function(){
35917         var ds = this.grid.ds, i, v = this.grid.view;
35918         var s = this.selections;
35919         s.each(function(r){
35920             if((i = ds.indexOfId(r.id)) != -1){
35921                 v.onRowSelect(i);
35922                 s.add(ds.getAt(i)); // updating the selection relate data
35923             }else{
35924                 s.remove(r);
35925             }
35926         });
35927     },
35928
35929     // private
35930     onRemove : function(v, index, r){
35931         this.selections.remove(r);
35932     },
35933
35934     // private
35935     onRowUpdated : function(v, index, r){
35936         if(this.isSelected(r)){
35937             v.onRowSelect(index);
35938         }
35939     },
35940
35941     /**
35942      * Select records.
35943      * @param {Array} records The records to select
35944      * @param {Boolean} keepExisting (optional) True to keep existing selections
35945      */
35946     selectRecords : function(records, keepExisting){
35947         if(!keepExisting){
35948             this.clearSelections();
35949         }
35950         var ds = this.grid.ds;
35951         for(var i = 0, len = records.length; i < len; i++){
35952             this.selectRow(ds.indexOf(records[i]), true);
35953         }
35954     },
35955
35956     /**
35957      * Gets the number of selected rows.
35958      * @return {Number}
35959      */
35960     getCount : function(){
35961         return this.selections.length;
35962     },
35963
35964     /**
35965      * Selects the first row in the grid.
35966      */
35967     selectFirstRow : function(){
35968         this.selectRow(0);
35969     },
35970
35971     /**
35972      * Select the last row.
35973      * @param {Boolean} keepExisting (optional) True to keep existing selections
35974      */
35975     selectLastRow : function(keepExisting){
35976         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
35977     },
35978
35979     /**
35980      * Selects the row immediately following the last selected row.
35981      * @param {Boolean} keepExisting (optional) True to keep existing selections
35982      */
35983     selectNext : function(keepExisting){
35984         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
35985             this.selectRow(this.last+1, keepExisting);
35986             var view = this.grid.view ? this.grid.view : this.grid;
35987             view.focusRow(this.last);
35988         }
35989     },
35990
35991     /**
35992      * Selects the row that precedes the last selected row.
35993      * @param {Boolean} keepExisting (optional) True to keep existing selections
35994      */
35995     selectPrevious : function(keepExisting){
35996         if(this.last){
35997             this.selectRow(this.last-1, keepExisting);
35998             var view = this.grid.view ? this.grid.view : this.grid;
35999             view.focusRow(this.last);
36000         }
36001     },
36002
36003     /**
36004      * Returns the selected records
36005      * @return {Array} Array of selected records
36006      */
36007     getSelections : function(){
36008         return [].concat(this.selections.items);
36009     },
36010
36011     /**
36012      * Returns the first selected record.
36013      * @return {Record}
36014      */
36015     getSelected : function(){
36016         return this.selections.itemAt(0);
36017     },
36018
36019
36020     /**
36021      * Clears all selections.
36022      */
36023     clearSelections : function(fast){
36024         if(this.locked) {
36025             return;
36026         }
36027         if(fast !== true){
36028             var ds = this.grid.ds;
36029             var s = this.selections;
36030             s.each(function(r){
36031                 this.deselectRow(ds.indexOfId(r.id));
36032             }, this);
36033             s.clear();
36034         }else{
36035             this.selections.clear();
36036         }
36037         this.last = false;
36038     },
36039
36040
36041     /**
36042      * Selects all rows.
36043      */
36044     selectAll : function(){
36045         if(this.locked) {
36046             return;
36047         }
36048         this.selections.clear();
36049         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36050             this.selectRow(i, true);
36051         }
36052     },
36053
36054     /**
36055      * Returns True if there is a selection.
36056      * @return {Boolean}
36057      */
36058     hasSelection : function(){
36059         return this.selections.length > 0;
36060     },
36061
36062     /**
36063      * Returns True if the specified row is selected.
36064      * @param {Number/Record} record The record or index of the record to check
36065      * @return {Boolean}
36066      */
36067     isSelected : function(index){
36068         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36069         return (r && this.selections.key(r.id) ? true : false);
36070     },
36071
36072     /**
36073      * Returns True if the specified record id is selected.
36074      * @param {String} id The id of record to check
36075      * @return {Boolean}
36076      */
36077     isIdSelected : function(id){
36078         return (this.selections.key(id) ? true : false);
36079     },
36080
36081     // private
36082     handleMouseDown : function(e, t)
36083     {
36084         var view = this.grid.view ? this.grid.view : this.grid;
36085         var rowIndex;
36086         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36087             return;
36088         };
36089         if(e.shiftKey && this.last !== false){
36090             var last = this.last;
36091             this.selectRange(last, rowIndex, e.ctrlKey);
36092             this.last = last; // reset the last
36093             view.focusRow(rowIndex);
36094         }else{
36095             var isSelected = this.isSelected(rowIndex);
36096             if(e.button !== 0 && isSelected){
36097                 view.focusRow(rowIndex);
36098             }else if(e.ctrlKey && isSelected){
36099                 this.deselectRow(rowIndex);
36100             }else if(!isSelected){
36101                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36102                 view.focusRow(rowIndex);
36103             }
36104         }
36105         this.fireEvent("afterselectionchange", this);
36106     },
36107     // private
36108     handleDragableRowClick :  function(grid, rowIndex, e) 
36109     {
36110         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36111             this.selectRow(rowIndex, false);
36112             var view = this.grid.view ? this.grid.view : this.grid;
36113             view.focusRow(rowIndex);
36114              this.fireEvent("afterselectionchange", this);
36115         }
36116     },
36117     
36118     /**
36119      * Selects multiple rows.
36120      * @param {Array} rows Array of the indexes of the row to select
36121      * @param {Boolean} keepExisting (optional) True to keep existing selections
36122      */
36123     selectRows : function(rows, keepExisting){
36124         if(!keepExisting){
36125             this.clearSelections();
36126         }
36127         for(var i = 0, len = rows.length; i < len; i++){
36128             this.selectRow(rows[i], true);
36129         }
36130     },
36131
36132     /**
36133      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36134      * @param {Number} startRow The index of the first row in the range
36135      * @param {Number} endRow The index of the last row in the range
36136      * @param {Boolean} keepExisting (optional) True to retain existing selections
36137      */
36138     selectRange : function(startRow, endRow, keepExisting){
36139         if(this.locked) {
36140             return;
36141         }
36142         if(!keepExisting){
36143             this.clearSelections();
36144         }
36145         if(startRow <= endRow){
36146             for(var i = startRow; i <= endRow; i++){
36147                 this.selectRow(i, true);
36148             }
36149         }else{
36150             for(var i = startRow; i >= endRow; i--){
36151                 this.selectRow(i, true);
36152             }
36153         }
36154     },
36155
36156     /**
36157      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36158      * @param {Number} startRow The index of the first row in the range
36159      * @param {Number} endRow The index of the last row in the range
36160      */
36161     deselectRange : function(startRow, endRow, preventViewNotify){
36162         if(this.locked) {
36163             return;
36164         }
36165         for(var i = startRow; i <= endRow; i++){
36166             this.deselectRow(i, preventViewNotify);
36167         }
36168     },
36169
36170     /**
36171      * Selects a row.
36172      * @param {Number} row The index of the row to select
36173      * @param {Boolean} keepExisting (optional) True to keep existing selections
36174      */
36175     selectRow : function(index, keepExisting, preventViewNotify){
36176         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36177             return;
36178         }
36179         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36180             if(!keepExisting || this.singleSelect){
36181                 this.clearSelections();
36182             }
36183             var r = this.grid.ds.getAt(index);
36184             this.selections.add(r);
36185             this.last = this.lastActive = index;
36186             if(!preventViewNotify){
36187                 var view = this.grid.view ? this.grid.view : this.grid;
36188                 view.onRowSelect(index);
36189             }
36190             this.fireEvent("rowselect", this, index, r);
36191             this.fireEvent("selectionchange", this);
36192         }
36193     },
36194
36195     /**
36196      * Deselects a row.
36197      * @param {Number} row The index of the row to deselect
36198      */
36199     deselectRow : function(index, preventViewNotify){
36200         if(this.locked) {
36201             return;
36202         }
36203         if(this.last == index){
36204             this.last = false;
36205         }
36206         if(this.lastActive == index){
36207             this.lastActive = false;
36208         }
36209         var r = this.grid.ds.getAt(index);
36210         this.selections.remove(r);
36211         if(!preventViewNotify){
36212             var view = this.grid.view ? this.grid.view : this.grid;
36213             view.onRowDeselect(index);
36214         }
36215         this.fireEvent("rowdeselect", this, index);
36216         this.fireEvent("selectionchange", this);
36217     },
36218
36219     // private
36220     restoreLast : function(){
36221         if(this._last){
36222             this.last = this._last;
36223         }
36224     },
36225
36226     // private
36227     acceptsNav : function(row, col, cm){
36228         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36229     },
36230
36231     // private
36232     onEditorKey : function(field, e){
36233         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36234         if(k == e.TAB){
36235             e.stopEvent();
36236             ed.completeEdit();
36237             if(e.shiftKey){
36238                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36239             }else{
36240                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36241             }
36242         }else if(k == e.ENTER && !e.ctrlKey){
36243             e.stopEvent();
36244             ed.completeEdit();
36245             if(e.shiftKey){
36246                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36247             }else{
36248                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36249             }
36250         }else if(k == e.ESC){
36251             ed.cancelEdit();
36252         }
36253         if(newCell){
36254             g.startEditing(newCell[0], newCell[1]);
36255         }
36256     }
36257 });/*
36258  * Based on:
36259  * Ext JS Library 1.1.1
36260  * Copyright(c) 2006-2007, Ext JS, LLC.
36261  *
36262  * Originally Released Under LGPL - original licence link has changed is not relivant.
36263  *
36264  * Fork - LGPL
36265  * <script type="text/javascript">
36266  */
36267 /**
36268  * @class Roo.grid.CellSelectionModel
36269  * @extends Roo.grid.AbstractSelectionModel
36270  * This class provides the basic implementation for cell selection in a grid.
36271  * @constructor
36272  * @param {Object} config The object containing the configuration of this model.
36273  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36274  */
36275 Roo.grid.CellSelectionModel = function(config){
36276     Roo.apply(this, config);
36277
36278     this.selection = null;
36279
36280     this.addEvents({
36281         /**
36282              * @event beforerowselect
36283              * Fires before a cell is selected.
36284              * @param {SelectionModel} this
36285              * @param {Number} rowIndex The selected row index
36286              * @param {Number} colIndex The selected cell index
36287              */
36288             "beforecellselect" : true,
36289         /**
36290              * @event cellselect
36291              * Fires when a cell is selected.
36292              * @param {SelectionModel} this
36293              * @param {Number} rowIndex The selected row index
36294              * @param {Number} colIndex The selected cell index
36295              */
36296             "cellselect" : true,
36297         /**
36298              * @event selectionchange
36299              * Fires when the active selection changes.
36300              * @param {SelectionModel} this
36301              * @param {Object} selection null for no selection or an object (o) with two properties
36302                 <ul>
36303                 <li>o.record: the record object for the row the selection is in</li>
36304                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36305                 </ul>
36306              */
36307             "selectionchange" : true,
36308         /**
36309              * @event tabend
36310              * Fires when the tab (or enter) was pressed on the last editable cell
36311              * You can use this to trigger add new row.
36312              * @param {SelectionModel} this
36313              */
36314             "tabend" : true,
36315          /**
36316              * @event beforeeditnext
36317              * Fires before the next editable sell is made active
36318              * You can use this to skip to another cell or fire the tabend
36319              *    if you set cell to false
36320              * @param {Object} eventdata object : { cell : [ row, col ] } 
36321              */
36322             "beforeeditnext" : true
36323     });
36324     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36325 };
36326
36327 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36328     
36329     enter_is_tab: false,
36330
36331     /** @ignore */
36332     initEvents : function(){
36333         this.grid.on("mousedown", this.handleMouseDown, this);
36334         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36335         var view = this.grid.view;
36336         view.on("refresh", this.onViewChange, this);
36337         view.on("rowupdated", this.onRowUpdated, this);
36338         view.on("beforerowremoved", this.clearSelections, this);
36339         view.on("beforerowsinserted", this.clearSelections, this);
36340         if(this.grid.isEditor){
36341             this.grid.on("beforeedit", this.beforeEdit,  this);
36342         }
36343     },
36344
36345         //private
36346     beforeEdit : function(e){
36347         this.select(e.row, e.column, false, true, e.record);
36348     },
36349
36350         //private
36351     onRowUpdated : function(v, index, r){
36352         if(this.selection && this.selection.record == r){
36353             v.onCellSelect(index, this.selection.cell[1]);
36354         }
36355     },
36356
36357         //private
36358     onViewChange : function(){
36359         this.clearSelections(true);
36360     },
36361
36362         /**
36363          * Returns the currently selected cell,.
36364          * @return {Array} The selected cell (row, column) or null if none selected.
36365          */
36366     getSelectedCell : function(){
36367         return this.selection ? this.selection.cell : null;
36368     },
36369
36370     /**
36371      * Clears all selections.
36372      * @param {Boolean} true to prevent the gridview from being notified about the change.
36373      */
36374     clearSelections : function(preventNotify){
36375         var s = this.selection;
36376         if(s){
36377             if(preventNotify !== true){
36378                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36379             }
36380             this.selection = null;
36381             this.fireEvent("selectionchange", this, null);
36382         }
36383     },
36384
36385     /**
36386      * Returns true if there is a selection.
36387      * @return {Boolean}
36388      */
36389     hasSelection : function(){
36390         return this.selection ? true : false;
36391     },
36392
36393     /** @ignore */
36394     handleMouseDown : function(e, t){
36395         var v = this.grid.getView();
36396         if(this.isLocked()){
36397             return;
36398         };
36399         var row = v.findRowIndex(t);
36400         var cell = v.findCellIndex(t);
36401         if(row !== false && cell !== false){
36402             this.select(row, cell);
36403         }
36404     },
36405
36406     /**
36407      * Selects a cell.
36408      * @param {Number} rowIndex
36409      * @param {Number} collIndex
36410      */
36411     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36412         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36413             this.clearSelections();
36414             r = r || this.grid.dataSource.getAt(rowIndex);
36415             this.selection = {
36416                 record : r,
36417                 cell : [rowIndex, colIndex]
36418             };
36419             if(!preventViewNotify){
36420                 var v = this.grid.getView();
36421                 v.onCellSelect(rowIndex, colIndex);
36422                 if(preventFocus !== true){
36423                     v.focusCell(rowIndex, colIndex);
36424                 }
36425             }
36426             this.fireEvent("cellselect", this, rowIndex, colIndex);
36427             this.fireEvent("selectionchange", this, this.selection);
36428         }
36429     },
36430
36431         //private
36432     isSelectable : function(rowIndex, colIndex, cm){
36433         return !cm.isHidden(colIndex);
36434     },
36435
36436     /** @ignore */
36437     handleKeyDown : function(e){
36438         //Roo.log('Cell Sel Model handleKeyDown');
36439         if(!e.isNavKeyPress()){
36440             return;
36441         }
36442         var g = this.grid, s = this.selection;
36443         if(!s){
36444             e.stopEvent();
36445             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36446             if(cell){
36447                 this.select(cell[0], cell[1]);
36448             }
36449             return;
36450         }
36451         var sm = this;
36452         var walk = function(row, col, step){
36453             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36454         };
36455         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36456         var newCell;
36457
36458       
36459
36460         switch(k){
36461             case e.TAB:
36462                 // handled by onEditorKey
36463                 if (g.isEditor && g.editing) {
36464                     return;
36465                 }
36466                 if(e.shiftKey) {
36467                     newCell = walk(r, c-1, -1);
36468                 } else {
36469                     newCell = walk(r, c+1, 1);
36470                 }
36471                 break;
36472             
36473             case e.DOWN:
36474                newCell = walk(r+1, c, 1);
36475                 break;
36476             
36477             case e.UP:
36478                 newCell = walk(r-1, c, -1);
36479                 break;
36480             
36481             case e.RIGHT:
36482                 newCell = walk(r, c+1, 1);
36483                 break;
36484             
36485             case e.LEFT:
36486                 newCell = walk(r, c-1, -1);
36487                 break;
36488             
36489             case e.ENTER:
36490                 
36491                 if(g.isEditor && !g.editing){
36492                    g.startEditing(r, c);
36493                    e.stopEvent();
36494                    return;
36495                 }
36496                 
36497                 
36498              break;
36499         };
36500         if(newCell){
36501             this.select(newCell[0], newCell[1]);
36502             e.stopEvent();
36503             
36504         }
36505     },
36506
36507     acceptsNav : function(row, col, cm){
36508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36509     },
36510     /**
36511      * Selects a cell.
36512      * @param {Number} field (not used) - as it's normally used as a listener
36513      * @param {Number} e - event - fake it by using
36514      *
36515      * var e = Roo.EventObjectImpl.prototype;
36516      * e.keyCode = e.TAB
36517      *
36518      * 
36519      */
36520     onEditorKey : function(field, e){
36521         
36522         var k = e.getKey(),
36523             newCell,
36524             g = this.grid,
36525             ed = g.activeEditor,
36526             forward = false;
36527         ///Roo.log('onEditorKey' + k);
36528         
36529         
36530         if (this.enter_is_tab && k == e.ENTER) {
36531             k = e.TAB;
36532         }
36533         
36534         if(k == e.TAB){
36535             if(e.shiftKey){
36536                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36537             }else{
36538                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36539                 forward = true;
36540             }
36541             
36542             e.stopEvent();
36543             
36544         } else if(k == e.ENTER &&  !e.ctrlKey){
36545             ed.completeEdit();
36546             e.stopEvent();
36547             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36548         
36549                 } else if(k == e.ESC){
36550             ed.cancelEdit();
36551         }
36552                 
36553         if (newCell) {
36554             var ecall = { cell : newCell, forward : forward };
36555             this.fireEvent('beforeeditnext', ecall );
36556             newCell = ecall.cell;
36557                         forward = ecall.forward;
36558         }
36559                 
36560         if(newCell){
36561             //Roo.log('next cell after edit');
36562             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36563         } else if (forward) {
36564             // tabbed past last
36565             this.fireEvent.defer(100, this, ['tabend',this]);
36566         }
36567     }
36568 });/*
36569  * Based on:
36570  * Ext JS Library 1.1.1
36571  * Copyright(c) 2006-2007, Ext JS, LLC.
36572  *
36573  * Originally Released Under LGPL - original licence link has changed is not relivant.
36574  *
36575  * Fork - LGPL
36576  * <script type="text/javascript">
36577  */
36578  
36579 /**
36580  * @class Roo.grid.EditorGrid
36581  * @extends Roo.grid.Grid
36582  * Class for creating and editable grid.
36583  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36584  * The container MUST have some type of size defined for the grid to fill. The container will be 
36585  * automatically set to position relative if it isn't already.
36586  * @param {Object} dataSource The data model to bind to
36587  * @param {Object} colModel The column model with info about this grid's columns
36588  */
36589 Roo.grid.EditorGrid = function(container, config){
36590     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36591     this.getGridEl().addClass("xedit-grid");
36592
36593     if(!this.selModel){
36594         this.selModel = new Roo.grid.CellSelectionModel();
36595     }
36596
36597     this.activeEditor = null;
36598
36599         this.addEvents({
36600             /**
36601              * @event beforeedit
36602              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36603              * <ul style="padding:5px;padding-left:16px;">
36604              * <li>grid - This grid</li>
36605              * <li>record - The record being edited</li>
36606              * <li>field - The field name being edited</li>
36607              * <li>value - The value for the field being edited.</li>
36608              * <li>row - The grid row index</li>
36609              * <li>column - The grid column index</li>
36610              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36611              * </ul>
36612              * @param {Object} e An edit event (see above for description)
36613              */
36614             "beforeedit" : true,
36615             /**
36616              * @event afteredit
36617              * Fires after a cell is edited. <br />
36618              * <ul style="padding:5px;padding-left:16px;">
36619              * <li>grid - This grid</li>
36620              * <li>record - The record being edited</li>
36621              * <li>field - The field name being edited</li>
36622              * <li>value - The value being set</li>
36623              * <li>originalValue - The original value for the field, before the edit.</li>
36624              * <li>row - The grid row index</li>
36625              * <li>column - The grid column index</li>
36626              * </ul>
36627              * @param {Object} e An edit event (see above for description)
36628              */
36629             "afteredit" : true,
36630             /**
36631              * @event validateedit
36632              * Fires after a cell is edited, but before the value is set in the record. 
36633          * You can use this to modify the value being set in the field, Return false
36634              * to cancel the change. The edit event object has the following properties <br />
36635              * <ul style="padding:5px;padding-left:16px;">
36636          * <li>editor - This editor</li>
36637              * <li>grid - This grid</li>
36638              * <li>record - The record being edited</li>
36639              * <li>field - The field name being edited</li>
36640              * <li>value - The value being set</li>
36641              * <li>originalValue - The original value for the field, before the edit.</li>
36642              * <li>row - The grid row index</li>
36643              * <li>column - The grid column index</li>
36644              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36645              * </ul>
36646              * @param {Object} e An edit event (see above for description)
36647              */
36648             "validateedit" : true
36649         });
36650     this.on("bodyscroll", this.stopEditing,  this);
36651     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36652 };
36653
36654 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36655     /**
36656      * @cfg {Number} clicksToEdit
36657      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36658      */
36659     clicksToEdit: 2,
36660
36661     // private
36662     isEditor : true,
36663     // private
36664     trackMouseOver: false, // causes very odd FF errors
36665
36666     onCellDblClick : function(g, row, col){
36667         this.startEditing(row, col);
36668     },
36669
36670     onEditComplete : function(ed, value, startValue){
36671         this.editing = false;
36672         this.activeEditor = null;
36673         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36674         var r = ed.record;
36675         var field = this.colModel.getDataIndex(ed.col);
36676         var e = {
36677             grid: this,
36678             record: r,
36679             field: field,
36680             originalValue: startValue,
36681             value: value,
36682             row: ed.row,
36683             column: ed.col,
36684             cancel:false,
36685             editor: ed
36686         };
36687         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36688         cell.show();
36689           
36690         if(String(value) !== String(startValue)){
36691             
36692             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36693                 r.set(field, e.value);
36694                 // if we are dealing with a combo box..
36695                 // then we also set the 'name' colum to be the displayField
36696                 if (ed.field.displayField && ed.field.name) {
36697                     r.set(ed.field.name, ed.field.el.dom.value);
36698                 }
36699                 
36700                 delete e.cancel; //?? why!!!
36701                 this.fireEvent("afteredit", e);
36702             }
36703         } else {
36704             this.fireEvent("afteredit", e); // always fire it!
36705         }
36706         this.view.focusCell(ed.row, ed.col);
36707     },
36708
36709     /**
36710      * Starts editing the specified for the specified row/column
36711      * @param {Number} rowIndex
36712      * @param {Number} colIndex
36713      */
36714     startEditing : function(row, col){
36715         this.stopEditing();
36716         if(this.colModel.isCellEditable(col, row)){
36717             this.view.ensureVisible(row, col, true);
36718           
36719             var r = this.dataSource.getAt(row);
36720             var field = this.colModel.getDataIndex(col);
36721             var cell = Roo.get(this.view.getCell(row,col));
36722             var e = {
36723                 grid: this,
36724                 record: r,
36725                 field: field,
36726                 value: r.data[field],
36727                 row: row,
36728                 column: col,
36729                 cancel:false 
36730             };
36731             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36732                 this.editing = true;
36733                 var ed = this.colModel.getCellEditor(col, row);
36734                 
36735                 if (!ed) {
36736                     return;
36737                 }
36738                 if(!ed.rendered){
36739                     ed.render(ed.parentEl || document.body);
36740                 }
36741                 ed.field.reset();
36742                
36743                 cell.hide();
36744                 
36745                 (function(){ // complex but required for focus issues in safari, ie and opera
36746                     ed.row = row;
36747                     ed.col = col;
36748                     ed.record = r;
36749                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36750                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36751                     this.activeEditor = ed;
36752                     var v = r.data[field];
36753                     ed.startEdit(this.view.getCell(row, col), v);
36754                     // combo's with 'displayField and name set
36755                     if (ed.field.displayField && ed.field.name) {
36756                         ed.field.el.dom.value = r.data[ed.field.name];
36757                     }
36758                     
36759                     
36760                 }).defer(50, this);
36761             }
36762         }
36763     },
36764         
36765     /**
36766      * Stops any active editing
36767      */
36768     stopEditing : function(){
36769         if(this.activeEditor){
36770             this.activeEditor.completeEdit();
36771         }
36772         this.activeEditor = null;
36773     },
36774         
36775          /**
36776      * Called to get grid's drag proxy text, by default returns this.ddText.
36777      * @return {String}
36778      */
36779     getDragDropText : function(){
36780         var count = this.selModel.getSelectedCell() ? 1 : 0;
36781         return String.format(this.ddText, count, count == 1 ? '' : 's');
36782     }
36783         
36784 });/*
36785  * Based on:
36786  * Ext JS Library 1.1.1
36787  * Copyright(c) 2006-2007, Ext JS, LLC.
36788  *
36789  * Originally Released Under LGPL - original licence link has changed is not relivant.
36790  *
36791  * Fork - LGPL
36792  * <script type="text/javascript">
36793  */
36794
36795 // private - not really -- you end up using it !
36796 // This is a support class used internally by the Grid components
36797
36798 /**
36799  * @class Roo.grid.GridEditor
36800  * @extends Roo.Editor
36801  * Class for creating and editable grid elements.
36802  * @param {Object} config any settings (must include field)
36803  */
36804 Roo.grid.GridEditor = function(field, config){
36805     if (!config && field.field) {
36806         config = field;
36807         field = Roo.factory(config.field, Roo.form);
36808     }
36809     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36810     field.monitorTab = false;
36811 };
36812
36813 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36814     
36815     /**
36816      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36817      */
36818     
36819     alignment: "tl-tl",
36820     autoSize: "width",
36821     hideEl : false,
36822     cls: "x-small-editor x-grid-editor",
36823     shim:false,
36824     shadow:"frame"
36825 });/*
36826  * Based on:
36827  * Ext JS Library 1.1.1
36828  * Copyright(c) 2006-2007, Ext JS, LLC.
36829  *
36830  * Originally Released Under LGPL - original licence link has changed is not relivant.
36831  *
36832  * Fork - LGPL
36833  * <script type="text/javascript">
36834  */
36835   
36836
36837   
36838 Roo.grid.PropertyRecord = Roo.data.Record.create([
36839     {name:'name',type:'string'},  'value'
36840 ]);
36841
36842
36843 Roo.grid.PropertyStore = function(grid, source){
36844     this.grid = grid;
36845     this.store = new Roo.data.Store({
36846         recordType : Roo.grid.PropertyRecord
36847     });
36848     this.store.on('update', this.onUpdate,  this);
36849     if(source){
36850         this.setSource(source);
36851     }
36852     Roo.grid.PropertyStore.superclass.constructor.call(this);
36853 };
36854
36855
36856
36857 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36858     setSource : function(o){
36859         this.source = o;
36860         this.store.removeAll();
36861         var data = [];
36862         for(var k in o){
36863             if(this.isEditableValue(o[k])){
36864                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36865             }
36866         }
36867         this.store.loadRecords({records: data}, {}, true);
36868     },
36869
36870     onUpdate : function(ds, record, type){
36871         if(type == Roo.data.Record.EDIT){
36872             var v = record.data['value'];
36873             var oldValue = record.modified['value'];
36874             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36875                 this.source[record.id] = v;
36876                 record.commit();
36877                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36878             }else{
36879                 record.reject();
36880             }
36881         }
36882     },
36883
36884     getProperty : function(row){
36885        return this.store.getAt(row);
36886     },
36887
36888     isEditableValue: function(val){
36889         if(val && val instanceof Date){
36890             return true;
36891         }else if(typeof val == 'object' || typeof val == 'function'){
36892             return false;
36893         }
36894         return true;
36895     },
36896
36897     setValue : function(prop, value){
36898         this.source[prop] = value;
36899         this.store.getById(prop).set('value', value);
36900     },
36901
36902     getSource : function(){
36903         return this.source;
36904     }
36905 });
36906
36907 Roo.grid.PropertyColumnModel = function(grid, store){
36908     this.grid = grid;
36909     var g = Roo.grid;
36910     g.PropertyColumnModel.superclass.constructor.call(this, [
36911         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36912         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36913     ]);
36914     this.store = store;
36915     this.bselect = Roo.DomHelper.append(document.body, {
36916         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36917             {tag: 'option', value: 'true', html: 'true'},
36918             {tag: 'option', value: 'false', html: 'false'}
36919         ]
36920     });
36921     Roo.id(this.bselect);
36922     var f = Roo.form;
36923     this.editors = {
36924         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36925         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36926         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36927         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36928         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36929     };
36930     this.renderCellDelegate = this.renderCell.createDelegate(this);
36931     this.renderPropDelegate = this.renderProp.createDelegate(this);
36932 };
36933
36934 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36935     
36936     
36937     nameText : 'Name',
36938     valueText : 'Value',
36939     
36940     dateFormat : 'm/j/Y',
36941     
36942     
36943     renderDate : function(dateVal){
36944         return dateVal.dateFormat(this.dateFormat);
36945     },
36946
36947     renderBool : function(bVal){
36948         return bVal ? 'true' : 'false';
36949     },
36950
36951     isCellEditable : function(colIndex, rowIndex){
36952         return colIndex == 1;
36953     },
36954
36955     getRenderer : function(col){
36956         return col == 1 ?
36957             this.renderCellDelegate : this.renderPropDelegate;
36958     },
36959
36960     renderProp : function(v){
36961         return this.getPropertyName(v);
36962     },
36963
36964     renderCell : function(val){
36965         var rv = val;
36966         if(val instanceof Date){
36967             rv = this.renderDate(val);
36968         }else if(typeof val == 'boolean'){
36969             rv = this.renderBool(val);
36970         }
36971         return Roo.util.Format.htmlEncode(rv);
36972     },
36973
36974     getPropertyName : function(name){
36975         var pn = this.grid.propertyNames;
36976         return pn && pn[name] ? pn[name] : name;
36977     },
36978
36979     getCellEditor : function(colIndex, rowIndex){
36980         var p = this.store.getProperty(rowIndex);
36981         var n = p.data['name'], val = p.data['value'];
36982         
36983         if(typeof(this.grid.customEditors[n]) == 'string'){
36984             return this.editors[this.grid.customEditors[n]];
36985         }
36986         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36987             return this.grid.customEditors[n];
36988         }
36989         if(val instanceof Date){
36990             return this.editors['date'];
36991         }else if(typeof val == 'number'){
36992             return this.editors['number'];
36993         }else if(typeof val == 'boolean'){
36994             return this.editors['boolean'];
36995         }else{
36996             return this.editors['string'];
36997         }
36998     }
36999 });
37000
37001 /**
37002  * @class Roo.grid.PropertyGrid
37003  * @extends Roo.grid.EditorGrid
37004  * This class represents the  interface of a component based property grid control.
37005  * <br><br>Usage:<pre><code>
37006  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37007       
37008  });
37009  // set any options
37010  grid.render();
37011  * </code></pre>
37012   
37013  * @constructor
37014  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37015  * The container MUST have some type of size defined for the grid to fill. The container will be
37016  * automatically set to position relative if it isn't already.
37017  * @param {Object} config A config object that sets properties on this grid.
37018  */
37019 Roo.grid.PropertyGrid = function(container, config){
37020     config = config || {};
37021     var store = new Roo.grid.PropertyStore(this);
37022     this.store = store;
37023     var cm = new Roo.grid.PropertyColumnModel(this, store);
37024     store.store.sort('name', 'ASC');
37025     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37026         ds: store.store,
37027         cm: cm,
37028         enableColLock:false,
37029         enableColumnMove:false,
37030         stripeRows:false,
37031         trackMouseOver: false,
37032         clicksToEdit:1
37033     }, config));
37034     this.getGridEl().addClass('x-props-grid');
37035     this.lastEditRow = null;
37036     this.on('columnresize', this.onColumnResize, this);
37037     this.addEvents({
37038          /**
37039              * @event beforepropertychange
37040              * Fires before a property changes (return false to stop?)
37041              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37042              * @param {String} id Record Id
37043              * @param {String} newval New Value
37044          * @param {String} oldval Old Value
37045              */
37046         "beforepropertychange": true,
37047         /**
37048              * @event propertychange
37049              * Fires after a property changes
37050              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37051              * @param {String} id Record Id
37052              * @param {String} newval New Value
37053          * @param {String} oldval Old Value
37054              */
37055         "propertychange": true
37056     });
37057     this.customEditors = this.customEditors || {};
37058 };
37059 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37060     
37061      /**
37062      * @cfg {Object} customEditors map of colnames=> custom editors.
37063      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37064      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37065      * false disables editing of the field.
37066          */
37067     
37068       /**
37069      * @cfg {Object} propertyNames map of property Names to their displayed value
37070          */
37071     
37072     render : function(){
37073         Roo.grid.PropertyGrid.superclass.render.call(this);
37074         this.autoSize.defer(100, this);
37075     },
37076
37077     autoSize : function(){
37078         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37079         if(this.view){
37080             this.view.fitColumns();
37081         }
37082     },
37083
37084     onColumnResize : function(){
37085         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37086         this.autoSize();
37087     },
37088     /**
37089      * Sets the data for the Grid
37090      * accepts a Key => Value object of all the elements avaiable.
37091      * @param {Object} data  to appear in grid.
37092      */
37093     setSource : function(source){
37094         this.store.setSource(source);
37095         //this.autoSize();
37096     },
37097     /**
37098      * Gets all the data from the grid.
37099      * @return {Object} data  data stored in grid
37100      */
37101     getSource : function(){
37102         return this.store.getSource();
37103     }
37104 });/*
37105   
37106  * Licence LGPL
37107  
37108  */
37109  
37110 /**
37111  * @class Roo.grid.Calendar
37112  * @extends Roo.util.Grid
37113  * This class extends the Grid to provide a calendar widget
37114  * <br><br>Usage:<pre><code>
37115  var grid = new Roo.grid.Calendar("my-container-id", {
37116      ds: myDataStore,
37117      cm: myColModel,
37118      selModel: mySelectionModel,
37119      autoSizeColumns: true,
37120      monitorWindowResize: false,
37121      trackMouseOver: true
37122      eventstore : real data store..
37123  });
37124  // set any options
37125  grid.render();
37126   
37127   * @constructor
37128  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37129  * The container MUST have some type of size defined for the grid to fill. The container will be
37130  * automatically set to position relative if it isn't already.
37131  * @param {Object} config A config object that sets properties on this grid.
37132  */
37133 Roo.grid.Calendar = function(container, config){
37134         // initialize the container
37135         this.container = Roo.get(container);
37136         this.container.update("");
37137         this.container.setStyle("overflow", "hidden");
37138     this.container.addClass('x-grid-container');
37139
37140     this.id = this.container.id;
37141
37142     Roo.apply(this, config);
37143     // check and correct shorthanded configs
37144     
37145     var rows = [];
37146     var d =1;
37147     for (var r = 0;r < 6;r++) {
37148         
37149         rows[r]=[];
37150         for (var c =0;c < 7;c++) {
37151             rows[r][c]= '';
37152         }
37153     }
37154     if (this.eventStore) {
37155         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37156         this.eventStore.on('load',this.onLoad, this);
37157         this.eventStore.on('beforeload',this.clearEvents, this);
37158          
37159     }
37160     
37161     this.dataSource = new Roo.data.Store({
37162             proxy: new Roo.data.MemoryProxy(rows),
37163             reader: new Roo.data.ArrayReader({}, [
37164                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37165     });
37166
37167     this.dataSource.load();
37168     this.ds = this.dataSource;
37169     this.ds.xmodule = this.xmodule || false;
37170     
37171     
37172     var cellRender = function(v,x,r)
37173     {
37174         return String.format(
37175             '<div class="fc-day  fc-widget-content"><div>' +
37176                 '<div class="fc-event-container"></div>' +
37177                 '<div class="fc-day-number">{0}</div>'+
37178                 
37179                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37180             '</div></div>', v);
37181     
37182     }
37183     
37184     
37185     this.colModel = new Roo.grid.ColumnModel( [
37186         {
37187             xtype: 'ColumnModel',
37188             xns: Roo.grid,
37189             dataIndex : 'weekday0',
37190             header : 'Sunday',
37191             renderer : cellRender
37192         },
37193         {
37194             xtype: 'ColumnModel',
37195             xns: Roo.grid,
37196             dataIndex : 'weekday1',
37197             header : 'Monday',
37198             renderer : cellRender
37199         },
37200         {
37201             xtype: 'ColumnModel',
37202             xns: Roo.grid,
37203             dataIndex : 'weekday2',
37204             header : 'Tuesday',
37205             renderer : cellRender
37206         },
37207         {
37208             xtype: 'ColumnModel',
37209             xns: Roo.grid,
37210             dataIndex : 'weekday3',
37211             header : 'Wednesday',
37212             renderer : cellRender
37213         },
37214         {
37215             xtype: 'ColumnModel',
37216             xns: Roo.grid,
37217             dataIndex : 'weekday4',
37218             header : 'Thursday',
37219             renderer : cellRender
37220         },
37221         {
37222             xtype: 'ColumnModel',
37223             xns: Roo.grid,
37224             dataIndex : 'weekday5',
37225             header : 'Friday',
37226             renderer : cellRender
37227         },
37228         {
37229             xtype: 'ColumnModel',
37230             xns: Roo.grid,
37231             dataIndex : 'weekday6',
37232             header : 'Saturday',
37233             renderer : cellRender
37234         }
37235     ]);
37236     this.cm = this.colModel;
37237     this.cm.xmodule = this.xmodule || false;
37238  
37239         
37240           
37241     //this.selModel = new Roo.grid.CellSelectionModel();
37242     //this.sm = this.selModel;
37243     //this.selModel.init(this);
37244     
37245     
37246     if(this.width){
37247         this.container.setWidth(this.width);
37248     }
37249
37250     if(this.height){
37251         this.container.setHeight(this.height);
37252     }
37253     /** @private */
37254         this.addEvents({
37255         // raw events
37256         /**
37257          * @event click
37258          * The raw click event for the entire grid.
37259          * @param {Roo.EventObject} e
37260          */
37261         "click" : true,
37262         /**
37263          * @event dblclick
37264          * The raw dblclick event for the entire grid.
37265          * @param {Roo.EventObject} e
37266          */
37267         "dblclick" : true,
37268         /**
37269          * @event contextmenu
37270          * The raw contextmenu event for the entire grid.
37271          * @param {Roo.EventObject} e
37272          */
37273         "contextmenu" : true,
37274         /**
37275          * @event mousedown
37276          * The raw mousedown event for the entire grid.
37277          * @param {Roo.EventObject} e
37278          */
37279         "mousedown" : true,
37280         /**
37281          * @event mouseup
37282          * The raw mouseup event for the entire grid.
37283          * @param {Roo.EventObject} e
37284          */
37285         "mouseup" : true,
37286         /**
37287          * @event mouseover
37288          * The raw mouseover event for the entire grid.
37289          * @param {Roo.EventObject} e
37290          */
37291         "mouseover" : true,
37292         /**
37293          * @event mouseout
37294          * The raw mouseout event for the entire grid.
37295          * @param {Roo.EventObject} e
37296          */
37297         "mouseout" : true,
37298         /**
37299          * @event keypress
37300          * The raw keypress event for the entire grid.
37301          * @param {Roo.EventObject} e
37302          */
37303         "keypress" : true,
37304         /**
37305          * @event keydown
37306          * The raw keydown event for the entire grid.
37307          * @param {Roo.EventObject} e
37308          */
37309         "keydown" : true,
37310
37311         // custom events
37312
37313         /**
37314          * @event cellclick
37315          * Fires when a cell is clicked
37316          * @param {Grid} this
37317          * @param {Number} rowIndex
37318          * @param {Number} columnIndex
37319          * @param {Roo.EventObject} e
37320          */
37321         "cellclick" : true,
37322         /**
37323          * @event celldblclick
37324          * Fires when a cell is double clicked
37325          * @param {Grid} this
37326          * @param {Number} rowIndex
37327          * @param {Number} columnIndex
37328          * @param {Roo.EventObject} e
37329          */
37330         "celldblclick" : true,
37331         /**
37332          * @event rowclick
37333          * Fires when a row is clicked
37334          * @param {Grid} this
37335          * @param {Number} rowIndex
37336          * @param {Roo.EventObject} e
37337          */
37338         "rowclick" : true,
37339         /**
37340          * @event rowdblclick
37341          * Fires when a row is double clicked
37342          * @param {Grid} this
37343          * @param {Number} rowIndex
37344          * @param {Roo.EventObject} e
37345          */
37346         "rowdblclick" : true,
37347         /**
37348          * @event headerclick
37349          * Fires when a header is clicked
37350          * @param {Grid} this
37351          * @param {Number} columnIndex
37352          * @param {Roo.EventObject} e
37353          */
37354         "headerclick" : true,
37355         /**
37356          * @event headerdblclick
37357          * Fires when a header cell is double clicked
37358          * @param {Grid} this
37359          * @param {Number} columnIndex
37360          * @param {Roo.EventObject} e
37361          */
37362         "headerdblclick" : true,
37363         /**
37364          * @event rowcontextmenu
37365          * Fires when a row is right clicked
37366          * @param {Grid} this
37367          * @param {Number} rowIndex
37368          * @param {Roo.EventObject} e
37369          */
37370         "rowcontextmenu" : true,
37371         /**
37372          * @event cellcontextmenu
37373          * Fires when a cell is right clicked
37374          * @param {Grid} this
37375          * @param {Number} rowIndex
37376          * @param {Number} cellIndex
37377          * @param {Roo.EventObject} e
37378          */
37379          "cellcontextmenu" : true,
37380         /**
37381          * @event headercontextmenu
37382          * Fires when a header is right clicked
37383          * @param {Grid} this
37384          * @param {Number} columnIndex
37385          * @param {Roo.EventObject} e
37386          */
37387         "headercontextmenu" : true,
37388         /**
37389          * @event bodyscroll
37390          * Fires when the body element is scrolled
37391          * @param {Number} scrollLeft
37392          * @param {Number} scrollTop
37393          */
37394         "bodyscroll" : true,
37395         /**
37396          * @event columnresize
37397          * Fires when the user resizes a column
37398          * @param {Number} columnIndex
37399          * @param {Number} newSize
37400          */
37401         "columnresize" : true,
37402         /**
37403          * @event columnmove
37404          * Fires when the user moves a column
37405          * @param {Number} oldIndex
37406          * @param {Number} newIndex
37407          */
37408         "columnmove" : true,
37409         /**
37410          * @event startdrag
37411          * Fires when row(s) start being dragged
37412          * @param {Grid} this
37413          * @param {Roo.GridDD} dd The drag drop object
37414          * @param {event} e The raw browser event
37415          */
37416         "startdrag" : true,
37417         /**
37418          * @event enddrag
37419          * Fires when a drag operation is complete
37420          * @param {Grid} this
37421          * @param {Roo.GridDD} dd The drag drop object
37422          * @param {event} e The raw browser event
37423          */
37424         "enddrag" : true,
37425         /**
37426          * @event dragdrop
37427          * Fires when dragged row(s) are dropped on a valid DD target
37428          * @param {Grid} this
37429          * @param {Roo.GridDD} dd The drag drop object
37430          * @param {String} targetId The target drag drop object
37431          * @param {event} e The raw browser event
37432          */
37433         "dragdrop" : true,
37434         /**
37435          * @event dragover
37436          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37437          * @param {Grid} this
37438          * @param {Roo.GridDD} dd The drag drop object
37439          * @param {String} targetId The target drag drop object
37440          * @param {event} e The raw browser event
37441          */
37442         "dragover" : true,
37443         /**
37444          * @event dragenter
37445          *  Fires when the dragged row(s) first cross another DD target while being dragged
37446          * @param {Grid} this
37447          * @param {Roo.GridDD} dd The drag drop object
37448          * @param {String} targetId The target drag drop object
37449          * @param {event} e The raw browser event
37450          */
37451         "dragenter" : true,
37452         /**
37453          * @event dragout
37454          * Fires when the dragged row(s) leave another DD target while being dragged
37455          * @param {Grid} this
37456          * @param {Roo.GridDD} dd The drag drop object
37457          * @param {String} targetId The target drag drop object
37458          * @param {event} e The raw browser event
37459          */
37460         "dragout" : true,
37461         /**
37462          * @event rowclass
37463          * Fires when a row is rendered, so you can change add a style to it.
37464          * @param {GridView} gridview   The grid view
37465          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37466          */
37467         'rowclass' : true,
37468
37469         /**
37470          * @event render
37471          * Fires when the grid is rendered
37472          * @param {Grid} grid
37473          */
37474         'render' : true,
37475             /**
37476              * @event select
37477              * Fires when a date is selected
37478              * @param {DatePicker} this
37479              * @param {Date} date The selected date
37480              */
37481         'select': true,
37482         /**
37483              * @event monthchange
37484              * Fires when the displayed month changes 
37485              * @param {DatePicker} this
37486              * @param {Date} date The selected month
37487              */
37488         'monthchange': true,
37489         /**
37490              * @event evententer
37491              * Fires when mouse over an event
37492              * @param {Calendar} this
37493              * @param {event} Event
37494              */
37495         'evententer': true,
37496         /**
37497              * @event eventleave
37498              * Fires when the mouse leaves an
37499              * @param {Calendar} this
37500              * @param {event}
37501              */
37502         'eventleave': true,
37503         /**
37504              * @event eventclick
37505              * Fires when the mouse click an
37506              * @param {Calendar} this
37507              * @param {event}
37508              */
37509         'eventclick': true,
37510         /**
37511              * @event eventrender
37512              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37513              * @param {Calendar} this
37514              * @param {data} data to be modified
37515              */
37516         'eventrender': true
37517         
37518     });
37519
37520     Roo.grid.Grid.superclass.constructor.call(this);
37521     this.on('render', function() {
37522         this.view.el.addClass('x-grid-cal'); 
37523         
37524         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37525
37526     },this);
37527     
37528     if (!Roo.grid.Calendar.style) {
37529         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37530             
37531             
37532             '.x-grid-cal .x-grid-col' :  {
37533                 height: 'auto !important',
37534                 'vertical-align': 'top'
37535             },
37536             '.x-grid-cal  .fc-event-hori' : {
37537                 height: '14px'
37538             }
37539              
37540             
37541         }, Roo.id());
37542     }
37543
37544     
37545     
37546 };
37547 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37548     /**
37549      * @cfg {Store} eventStore The store that loads events.
37550      */
37551     eventStore : 25,
37552
37553      
37554     activeDate : false,
37555     startDay : 0,
37556     autoWidth : true,
37557     monitorWindowResize : false,
37558
37559     
37560     resizeColumns : function() {
37561         var col = (this.view.el.getWidth() / 7) - 3;
37562         // loop through cols, and setWidth
37563         for(var i =0 ; i < 7 ; i++){
37564             this.cm.setColumnWidth(i, col);
37565         }
37566     },
37567      setDate :function(date) {
37568         
37569         Roo.log('setDate?');
37570         
37571         this.resizeColumns();
37572         var vd = this.activeDate;
37573         this.activeDate = date;
37574 //        if(vd && this.el){
37575 //            var t = date.getTime();
37576 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37577 //                Roo.log('using add remove');
37578 //                
37579 //                this.fireEvent('monthchange', this, date);
37580 //                
37581 //                this.cells.removeClass("fc-state-highlight");
37582 //                this.cells.each(function(c){
37583 //                   if(c.dateValue == t){
37584 //                       c.addClass("fc-state-highlight");
37585 //                       setTimeout(function(){
37586 //                            try{c.dom.firstChild.focus();}catch(e){}
37587 //                       }, 50);
37588 //                       return false;
37589 //                   }
37590 //                   return true;
37591 //                });
37592 //                return;
37593 //            }
37594 //        }
37595         
37596         var days = date.getDaysInMonth();
37597         
37598         var firstOfMonth = date.getFirstDateOfMonth();
37599         var startingPos = firstOfMonth.getDay()-this.startDay;
37600         
37601         if(startingPos < this.startDay){
37602             startingPos += 7;
37603         }
37604         
37605         var pm = date.add(Date.MONTH, -1);
37606         var prevStart = pm.getDaysInMonth()-startingPos;
37607 //        
37608         
37609         
37610         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37611         
37612         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37613         //this.cells.addClassOnOver('fc-state-hover');
37614         
37615         var cells = this.cells.elements;
37616         var textEls = this.textNodes;
37617         
37618         //Roo.each(cells, function(cell){
37619         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37620         //});
37621         
37622         days += startingPos;
37623
37624         // convert everything to numbers so it's fast
37625         var day = 86400000;
37626         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37627         //Roo.log(d);
37628         //Roo.log(pm);
37629         //Roo.log(prevStart);
37630         
37631         var today = new Date().clearTime().getTime();
37632         var sel = date.clearTime().getTime();
37633         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37634         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37635         var ddMatch = this.disabledDatesRE;
37636         var ddText = this.disabledDatesText;
37637         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37638         var ddaysText = this.disabledDaysText;
37639         var format = this.format;
37640         
37641         var setCellClass = function(cal, cell){
37642             
37643             //Roo.log('set Cell Class');
37644             cell.title = "";
37645             var t = d.getTime();
37646             
37647             //Roo.log(d);
37648             
37649             
37650             cell.dateValue = t;
37651             if(t == today){
37652                 cell.className += " fc-today";
37653                 cell.className += " fc-state-highlight";
37654                 cell.title = cal.todayText;
37655             }
37656             if(t == sel){
37657                 // disable highlight in other month..
37658                 cell.className += " fc-state-highlight";
37659                 
37660             }
37661             // disabling
37662             if(t < min) {
37663                 //cell.className = " fc-state-disabled";
37664                 cell.title = cal.minText;
37665                 return;
37666             }
37667             if(t > max) {
37668                 //cell.className = " fc-state-disabled";
37669                 cell.title = cal.maxText;
37670                 return;
37671             }
37672             if(ddays){
37673                 if(ddays.indexOf(d.getDay()) != -1){
37674                     // cell.title = ddaysText;
37675                    // cell.className = " fc-state-disabled";
37676                 }
37677             }
37678             if(ddMatch && format){
37679                 var fvalue = d.dateFormat(format);
37680                 if(ddMatch.test(fvalue)){
37681                     cell.title = ddText.replace("%0", fvalue);
37682                    cell.className = " fc-state-disabled";
37683                 }
37684             }
37685             
37686             if (!cell.initialClassName) {
37687                 cell.initialClassName = cell.dom.className;
37688             }
37689             
37690             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37691         };
37692
37693         var i = 0;
37694         
37695         for(; i < startingPos; i++) {
37696             cells[i].dayName =  (++prevStart);
37697             Roo.log(textEls[i]);
37698             d.setDate(d.getDate()+1);
37699             
37700             //cells[i].className = "fc-past fc-other-month";
37701             setCellClass(this, cells[i]);
37702         }
37703         
37704         var intDay = 0;
37705         
37706         for(; i < days; i++){
37707             intDay = i - startingPos + 1;
37708             cells[i].dayName =  (intDay);
37709             d.setDate(d.getDate()+1);
37710             
37711             cells[i].className = ''; // "x-date-active";
37712             setCellClass(this, cells[i]);
37713         }
37714         var extraDays = 0;
37715         
37716         for(; i < 42; i++) {
37717             //textEls[i].innerHTML = (++extraDays);
37718             
37719             d.setDate(d.getDate()+1);
37720             cells[i].dayName = (++extraDays);
37721             cells[i].className = "fc-future fc-other-month";
37722             setCellClass(this, cells[i]);
37723         }
37724         
37725         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37726         
37727         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37728         
37729         // this will cause all the cells to mis
37730         var rows= [];
37731         var i =0;
37732         for (var r = 0;r < 6;r++) {
37733             for (var c =0;c < 7;c++) {
37734                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37735             }    
37736         }
37737         
37738         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37739         for(i=0;i<cells.length;i++) {
37740             
37741             this.cells.elements[i].dayName = cells[i].dayName ;
37742             this.cells.elements[i].className = cells[i].className;
37743             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37744             this.cells.elements[i].title = cells[i].title ;
37745             this.cells.elements[i].dateValue = cells[i].dateValue ;
37746         }
37747         
37748         
37749         
37750         
37751         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37752         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37753         
37754         ////if(totalRows != 6){
37755             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37756            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37757        // }
37758         
37759         this.fireEvent('monthchange', this, date);
37760         
37761         
37762     },
37763  /**
37764      * Returns the grid's SelectionModel.
37765      * @return {SelectionModel}
37766      */
37767     getSelectionModel : function(){
37768         if(!this.selModel){
37769             this.selModel = new Roo.grid.CellSelectionModel();
37770         }
37771         return this.selModel;
37772     },
37773
37774     load: function() {
37775         this.eventStore.load()
37776         
37777         
37778         
37779     },
37780     
37781     findCell : function(dt) {
37782         dt = dt.clearTime().getTime();
37783         var ret = false;
37784         this.cells.each(function(c){
37785             //Roo.log("check " +c.dateValue + '?=' + dt);
37786             if(c.dateValue == dt){
37787                 ret = c;
37788                 return false;
37789             }
37790             return true;
37791         });
37792         
37793         return ret;
37794     },
37795     
37796     findCells : function(rec) {
37797         var s = rec.data.start_dt.clone().clearTime().getTime();
37798        // Roo.log(s);
37799         var e= rec.data.end_dt.clone().clearTime().getTime();
37800        // Roo.log(e);
37801         var ret = [];
37802         this.cells.each(function(c){
37803              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37804             
37805             if(c.dateValue > e){
37806                 return ;
37807             }
37808             if(c.dateValue < s){
37809                 return ;
37810             }
37811             ret.push(c);
37812         });
37813         
37814         return ret;    
37815     },
37816     
37817     findBestRow: function(cells)
37818     {
37819         var ret = 0;
37820         
37821         for (var i =0 ; i < cells.length;i++) {
37822             ret  = Math.max(cells[i].rows || 0,ret);
37823         }
37824         return ret;
37825         
37826     },
37827     
37828     
37829     addItem : function(rec)
37830     {
37831         // look for vertical location slot in
37832         var cells = this.findCells(rec);
37833         
37834         rec.row = this.findBestRow(cells);
37835         
37836         // work out the location.
37837         
37838         var crow = false;
37839         var rows = [];
37840         for(var i =0; i < cells.length; i++) {
37841             if (!crow) {
37842                 crow = {
37843                     start : cells[i],
37844                     end :  cells[i]
37845                 };
37846                 continue;
37847             }
37848             if (crow.start.getY() == cells[i].getY()) {
37849                 // on same row.
37850                 crow.end = cells[i];
37851                 continue;
37852             }
37853             // different row.
37854             rows.push(crow);
37855             crow = {
37856                 start: cells[i],
37857                 end : cells[i]
37858             };
37859             
37860         }
37861         
37862         rows.push(crow);
37863         rec.els = [];
37864         rec.rows = rows;
37865         rec.cells = cells;
37866         for (var i = 0; i < cells.length;i++) {
37867             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37868             
37869         }
37870         
37871         
37872     },
37873     
37874     clearEvents: function() {
37875         
37876         if (!this.eventStore.getCount()) {
37877             return;
37878         }
37879         // reset number of rows in cells.
37880         Roo.each(this.cells.elements, function(c){
37881             c.rows = 0;
37882         });
37883         
37884         this.eventStore.each(function(e) {
37885             this.clearEvent(e);
37886         },this);
37887         
37888     },
37889     
37890     clearEvent : function(ev)
37891     {
37892         if (ev.els) {
37893             Roo.each(ev.els, function(el) {
37894                 el.un('mouseenter' ,this.onEventEnter, this);
37895                 el.un('mouseleave' ,this.onEventLeave, this);
37896                 el.remove();
37897             },this);
37898             ev.els = [];
37899         }
37900     },
37901     
37902     
37903     renderEvent : function(ev,ctr) {
37904         if (!ctr) {
37905              ctr = this.view.el.select('.fc-event-container',true).first();
37906         }
37907         
37908          
37909         this.clearEvent(ev);
37910             //code
37911        
37912         
37913         
37914         ev.els = [];
37915         var cells = ev.cells;
37916         var rows = ev.rows;
37917         this.fireEvent('eventrender', this, ev);
37918         
37919         for(var i =0; i < rows.length; i++) {
37920             
37921             cls = '';
37922             if (i == 0) {
37923                 cls += ' fc-event-start';
37924             }
37925             if ((i+1) == rows.length) {
37926                 cls += ' fc-event-end';
37927             }
37928             
37929             //Roo.log(ev.data);
37930             // how many rows should it span..
37931             var cg = this.eventTmpl.append(ctr,Roo.apply({
37932                 fccls : cls
37933                 
37934             }, ev.data) , true);
37935             
37936             
37937             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37938             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37939             cg.on('click', this.onEventClick, this, ev);
37940             
37941             ev.els.push(cg);
37942             
37943             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37944             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37945             //Roo.log(cg);
37946              
37947             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37948             cg.setWidth(ebox.right - sbox.x -2);
37949         }
37950     },
37951     
37952     renderEvents: function()
37953     {   
37954         // first make sure there is enough space..
37955         
37956         if (!this.eventTmpl) {
37957             this.eventTmpl = new Roo.Template(
37958                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37959                     '<div class="fc-event-inner">' +
37960                         '<span class="fc-event-time">{time}</span>' +
37961                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37962                     '</div>' +
37963                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37964                 '</div>'
37965             );
37966                 
37967         }
37968                
37969         
37970         
37971         this.cells.each(function(c) {
37972             //Roo.log(c.select('.fc-day-content div',true).first());
37973             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37974         });
37975         
37976         var ctr = this.view.el.select('.fc-event-container',true).first();
37977         
37978         var cls;
37979         this.eventStore.each(function(ev){
37980             
37981             this.renderEvent(ev);
37982              
37983              
37984         }, this);
37985         this.view.layout();
37986         
37987     },
37988     
37989     onEventEnter: function (e, el,event,d) {
37990         this.fireEvent('evententer', this, el, event);
37991     },
37992     
37993     onEventLeave: function (e, el,event,d) {
37994         this.fireEvent('eventleave', this, el, event);
37995     },
37996     
37997     onEventClick: function (e, el,event,d) {
37998         this.fireEvent('eventclick', this, el, event);
37999     },
38000     
38001     onMonthChange: function () {
38002         this.store.load();
38003     },
38004     
38005     onLoad: function () {
38006         
38007         //Roo.log('calendar onload');
38008 //         
38009         if(this.eventStore.getCount() > 0){
38010             
38011            
38012             
38013             this.eventStore.each(function(d){
38014                 
38015                 
38016                 // FIXME..
38017                 var add =   d.data;
38018                 if (typeof(add.end_dt) == 'undefined')  {
38019                     Roo.log("Missing End time in calendar data: ");
38020                     Roo.log(d);
38021                     return;
38022                 }
38023                 if (typeof(add.start_dt) == 'undefined')  {
38024                     Roo.log("Missing Start time in calendar data: ");
38025                     Roo.log(d);
38026                     return;
38027                 }
38028                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38029                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38030                 add.id = add.id || d.id;
38031                 add.title = add.title || '??';
38032                 
38033                 this.addItem(d);
38034                 
38035              
38036             },this);
38037         }
38038         
38039         this.renderEvents();
38040     }
38041     
38042
38043 });
38044 /*
38045  grid : {
38046                 xtype: 'Grid',
38047                 xns: Roo.grid,
38048                 listeners : {
38049                     render : function ()
38050                     {
38051                         _this.grid = this;
38052                         
38053                         if (!this.view.el.hasClass('course-timesheet')) {
38054                             this.view.el.addClass('course-timesheet');
38055                         }
38056                         if (this.tsStyle) {
38057                             this.ds.load({});
38058                             return; 
38059                         }
38060                         Roo.log('width');
38061                         Roo.log(_this.grid.view.el.getWidth());
38062                         
38063                         
38064                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38065                             '.course-timesheet .x-grid-row' : {
38066                                 height: '80px'
38067                             },
38068                             '.x-grid-row td' : {
38069                                 'vertical-align' : 0
38070                             },
38071                             '.course-edit-link' : {
38072                                 'color' : 'blue',
38073                                 'text-overflow' : 'ellipsis',
38074                                 'overflow' : 'hidden',
38075                                 'white-space' : 'nowrap',
38076                                 'cursor' : 'pointer'
38077                             },
38078                             '.sub-link' : {
38079                                 'color' : 'green'
38080                             },
38081                             '.de-act-sup-link' : {
38082                                 'color' : 'purple',
38083                                 'text-decoration' : 'line-through'
38084                             },
38085                             '.de-act-link' : {
38086                                 'color' : 'red',
38087                                 'text-decoration' : 'line-through'
38088                             },
38089                             '.course-timesheet .course-highlight' : {
38090                                 'border-top-style': 'dashed !important',
38091                                 'border-bottom-bottom': 'dashed !important'
38092                             },
38093                             '.course-timesheet .course-item' : {
38094                                 'font-family'   : 'tahoma, arial, helvetica',
38095                                 'font-size'     : '11px',
38096                                 'overflow'      : 'hidden',
38097                                 'padding-left'  : '10px',
38098                                 'padding-right' : '10px',
38099                                 'padding-top' : '10px' 
38100                             }
38101                             
38102                         }, Roo.id());
38103                                 this.ds.load({});
38104                     }
38105                 },
38106                 autoWidth : true,
38107                 monitorWindowResize : false,
38108                 cellrenderer : function(v,x,r)
38109                 {
38110                     return v;
38111                 },
38112                 sm : {
38113                     xtype: 'CellSelectionModel',
38114                     xns: Roo.grid
38115                 },
38116                 dataSource : {
38117                     xtype: 'Store',
38118                     xns: Roo.data,
38119                     listeners : {
38120                         beforeload : function (_self, options)
38121                         {
38122                             options.params = options.params || {};
38123                             options.params._month = _this.monthField.getValue();
38124                             options.params.limit = 9999;
38125                             options.params['sort'] = 'when_dt';    
38126                             options.params['dir'] = 'ASC';    
38127                             this.proxy.loadResponse = this.loadResponse;
38128                             Roo.log("load?");
38129                             //this.addColumns();
38130                         },
38131                         load : function (_self, records, options)
38132                         {
38133                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38134                                 // if you click on the translation.. you can edit it...
38135                                 var el = Roo.get(this);
38136                                 var id = el.dom.getAttribute('data-id');
38137                                 var d = el.dom.getAttribute('data-date');
38138                                 var t = el.dom.getAttribute('data-time');
38139                                 //var id = this.child('span').dom.textContent;
38140                                 
38141                                 //Roo.log(this);
38142                                 Pman.Dialog.CourseCalendar.show({
38143                                     id : id,
38144                                     when_d : d,
38145                                     when_t : t,
38146                                     productitem_active : id ? 1 : 0
38147                                 }, function() {
38148                                     _this.grid.ds.load({});
38149                                 });
38150                            
38151                            });
38152                            
38153                            _this.panel.fireEvent('resize', [ '', '' ]);
38154                         }
38155                     },
38156                     loadResponse : function(o, success, response){
38157                             // this is overridden on before load..
38158                             
38159                             Roo.log("our code?");       
38160                             //Roo.log(success);
38161                             //Roo.log(response)
38162                             delete this.activeRequest;
38163                             if(!success){
38164                                 this.fireEvent("loadexception", this, o, response);
38165                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38166                                 return;
38167                             }
38168                             var result;
38169                             try {
38170                                 result = o.reader.read(response);
38171                             }catch(e){
38172                                 Roo.log("load exception?");
38173                                 this.fireEvent("loadexception", this, o, response, e);
38174                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38175                                 return;
38176                             }
38177                             Roo.log("ready...");        
38178                             // loop through result.records;
38179                             // and set this.tdate[date] = [] << array of records..
38180                             _this.tdata  = {};
38181                             Roo.each(result.records, function(r){
38182                                 //Roo.log(r.data);
38183                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38184                                     _this.tdata[r.data.when_dt.format('j')] = [];
38185                                 }
38186                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38187                             });
38188                             
38189                             //Roo.log(_this.tdata);
38190                             
38191                             result.records = [];
38192                             result.totalRecords = 6;
38193                     
38194                             // let's generate some duumy records for the rows.
38195                             //var st = _this.dateField.getValue();
38196                             
38197                             // work out monday..
38198                             //st = st.add(Date.DAY, -1 * st.format('w'));
38199                             
38200                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38201                             
38202                             var firstOfMonth = date.getFirstDayOfMonth();
38203                             var days = date.getDaysInMonth();
38204                             var d = 1;
38205                             var firstAdded = false;
38206                             for (var i = 0; i < result.totalRecords ; i++) {
38207                                 //var d= st.add(Date.DAY, i);
38208                                 var row = {};
38209                                 var added = 0;
38210                                 for(var w = 0 ; w < 7 ; w++){
38211                                     if(!firstAdded && firstOfMonth != w){
38212                                         continue;
38213                                     }
38214                                     if(d > days){
38215                                         continue;
38216                                     }
38217                                     firstAdded = true;
38218                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38219                                     row['weekday'+w] = String.format(
38220                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38221                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38222                                                     d,
38223                                                     date.format('Y-m-')+dd
38224                                                 );
38225                                     added++;
38226                                     if(typeof(_this.tdata[d]) != 'undefined'){
38227                                         Roo.each(_this.tdata[d], function(r){
38228                                             var is_sub = '';
38229                                             var deactive = '';
38230                                             var id = r.id;
38231                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38232                                             if(r.parent_id*1>0){
38233                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38234                                                 id = r.parent_id;
38235                                             }
38236                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38237                                                 deactive = 'de-act-link';
38238                                             }
38239                                             
38240                                             row['weekday'+w] += String.format(
38241                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38242                                                     id, //0
38243                                                     r.product_id_name, //1
38244                                                     r.when_dt.format('h:ia'), //2
38245                                                     is_sub, //3
38246                                                     deactive, //4
38247                                                     desc // 5
38248                                             );
38249                                         });
38250                                     }
38251                                     d++;
38252                                 }
38253                                 
38254                                 // only do this if something added..
38255                                 if(added > 0){ 
38256                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38257                                 }
38258                                 
38259                                 
38260                                 // push it twice. (second one with an hour..
38261                                 
38262                             }
38263                             //Roo.log(result);
38264                             this.fireEvent("load", this, o, o.request.arg);
38265                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38266                         },
38267                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38268                     proxy : {
38269                         xtype: 'HttpProxy',
38270                         xns: Roo.data,
38271                         method : 'GET',
38272                         url : baseURL + '/Roo/Shop_course.php'
38273                     },
38274                     reader : {
38275                         xtype: 'JsonReader',
38276                         xns: Roo.data,
38277                         id : 'id',
38278                         fields : [
38279                             {
38280                                 'name': 'id',
38281                                 'type': 'int'
38282                             },
38283                             {
38284                                 'name': 'when_dt',
38285                                 'type': 'string'
38286                             },
38287                             {
38288                                 'name': 'end_dt',
38289                                 'type': 'string'
38290                             },
38291                             {
38292                                 'name': 'parent_id',
38293                                 'type': 'int'
38294                             },
38295                             {
38296                                 'name': 'product_id',
38297                                 'type': 'int'
38298                             },
38299                             {
38300                                 'name': 'productitem_id',
38301                                 'type': 'int'
38302                             },
38303                             {
38304                                 'name': 'guid',
38305                                 'type': 'int'
38306                             }
38307                         ]
38308                     }
38309                 },
38310                 toolbar : {
38311                     xtype: 'Toolbar',
38312                     xns: Roo,
38313                     items : [
38314                         {
38315                             xtype: 'Button',
38316                             xns: Roo.Toolbar,
38317                             listeners : {
38318                                 click : function (_self, e)
38319                                 {
38320                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38321                                     sd.setMonth(sd.getMonth()-1);
38322                                     _this.monthField.setValue(sd.format('Y-m-d'));
38323                                     _this.grid.ds.load({});
38324                                 }
38325                             },
38326                             text : "Back"
38327                         },
38328                         {
38329                             xtype: 'Separator',
38330                             xns: Roo.Toolbar
38331                         },
38332                         {
38333                             xtype: 'MonthField',
38334                             xns: Roo.form,
38335                             listeners : {
38336                                 render : function (_self)
38337                                 {
38338                                     _this.monthField = _self;
38339                                    // _this.monthField.set  today
38340                                 },
38341                                 select : function (combo, date)
38342                                 {
38343                                     _this.grid.ds.load({});
38344                                 }
38345                             },
38346                             value : (function() { return new Date(); })()
38347                         },
38348                         {
38349                             xtype: 'Separator',
38350                             xns: Roo.Toolbar
38351                         },
38352                         {
38353                             xtype: 'TextItem',
38354                             xns: Roo.Toolbar,
38355                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38356                         },
38357                         {
38358                             xtype: 'Fill',
38359                             xns: Roo.Toolbar
38360                         },
38361                         {
38362                             xtype: 'Button',
38363                             xns: Roo.Toolbar,
38364                             listeners : {
38365                                 click : function (_self, e)
38366                                 {
38367                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38368                                     sd.setMonth(sd.getMonth()+1);
38369                                     _this.monthField.setValue(sd.format('Y-m-d'));
38370                                     _this.grid.ds.load({});
38371                                 }
38372                             },
38373                             text : "Next"
38374                         }
38375                     ]
38376                 },
38377                  
38378             }
38379         };
38380         
38381         *//*
38382  * Based on:
38383  * Ext JS Library 1.1.1
38384  * Copyright(c) 2006-2007, Ext JS, LLC.
38385  *
38386  * Originally Released Under LGPL - original licence link has changed is not relivant.
38387  *
38388  * Fork - LGPL
38389  * <script type="text/javascript">
38390  */
38391  
38392 /**
38393  * @class Roo.LoadMask
38394  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38395  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38396  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38397  * element's UpdateManager load indicator and will be destroyed after the initial load.
38398  * @constructor
38399  * Create a new LoadMask
38400  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38401  * @param {Object} config The config object
38402  */
38403 Roo.LoadMask = function(el, config){
38404     this.el = Roo.get(el);
38405     Roo.apply(this, config);
38406     if(this.store){
38407         this.store.on('beforeload', this.onBeforeLoad, this);
38408         this.store.on('load', this.onLoad, this);
38409         this.store.on('loadexception', this.onLoadException, this);
38410         this.removeMask = false;
38411     }else{
38412         var um = this.el.getUpdateManager();
38413         um.showLoadIndicator = false; // disable the default indicator
38414         um.on('beforeupdate', this.onBeforeLoad, this);
38415         um.on('update', this.onLoad, this);
38416         um.on('failure', this.onLoad, this);
38417         this.removeMask = true;
38418     }
38419 };
38420
38421 Roo.LoadMask.prototype = {
38422     /**
38423      * @cfg {Boolean} removeMask
38424      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38425      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38426      */
38427     removeMask : false,
38428     /**
38429      * @cfg {String} msg
38430      * The text to display in a centered loading message box (defaults to 'Loading...')
38431      */
38432     msg : 'Loading...',
38433     /**
38434      * @cfg {String} msgCls
38435      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38436      */
38437     msgCls : 'x-mask-loading',
38438
38439     /**
38440      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38441      * @type Boolean
38442      */
38443     disabled: false,
38444
38445     /**
38446      * Disables the mask to prevent it from being displayed
38447      */
38448     disable : function(){
38449        this.disabled = true;
38450     },
38451
38452     /**
38453      * Enables the mask so that it can be displayed
38454      */
38455     enable : function(){
38456         this.disabled = false;
38457     },
38458     
38459     onLoadException : function()
38460     {
38461         Roo.log(arguments);
38462         
38463         if (typeof(arguments[3]) != 'undefined') {
38464             Roo.MessageBox.alert("Error loading",arguments[3]);
38465         } 
38466         /*
38467         try {
38468             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38469                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38470             }   
38471         } catch(e) {
38472             
38473         }
38474         */
38475     
38476         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38477     },
38478     // private
38479     onLoad : function()
38480     {
38481         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38482     },
38483
38484     // private
38485     onBeforeLoad : function(){
38486         if(!this.disabled){
38487             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38488         }
38489     },
38490
38491     // private
38492     destroy : function(){
38493         if(this.store){
38494             this.store.un('beforeload', this.onBeforeLoad, this);
38495             this.store.un('load', this.onLoad, this);
38496             this.store.un('loadexception', this.onLoadException, this);
38497         }else{
38498             var um = this.el.getUpdateManager();
38499             um.un('beforeupdate', this.onBeforeLoad, this);
38500             um.un('update', this.onLoad, this);
38501             um.un('failure', this.onLoad, this);
38502         }
38503     }
38504 };/*
38505  * Based on:
38506  * Ext JS Library 1.1.1
38507  * Copyright(c) 2006-2007, Ext JS, LLC.
38508  *
38509  * Originally Released Under LGPL - original licence link has changed is not relivant.
38510  *
38511  * Fork - LGPL
38512  * <script type="text/javascript">
38513  */
38514
38515
38516 /**
38517  * @class Roo.XTemplate
38518  * @extends Roo.Template
38519  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38520 <pre><code>
38521 var t = new Roo.XTemplate(
38522         '&lt;select name="{name}"&gt;',
38523                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38524         '&lt;/select&gt;'
38525 );
38526  
38527 // then append, applying the master template values
38528  </code></pre>
38529  *
38530  * Supported features:
38531  *
38532  *  Tags:
38533
38534 <pre><code>
38535       {a_variable} - output encoded.
38536       {a_variable.format:("Y-m-d")} - call a method on the variable
38537       {a_variable:raw} - unencoded output
38538       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38539       {a_variable:this.method_on_template(...)} - call a method on the template object.
38540  
38541 </code></pre>
38542  *  The tpl tag:
38543 <pre><code>
38544         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38545         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38546         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38547         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38548   
38549         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38550         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38551 </code></pre>
38552  *      
38553  */
38554 Roo.XTemplate = function()
38555 {
38556     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38557     if (this.html) {
38558         this.compile();
38559     }
38560 };
38561
38562
38563 Roo.extend(Roo.XTemplate, Roo.Template, {
38564
38565     /**
38566      * The various sub templates
38567      */
38568     tpls : false,
38569     /**
38570      *
38571      * basic tag replacing syntax
38572      * WORD:WORD()
38573      *
38574      * // you can fake an object call by doing this
38575      *  x.t:(test,tesT) 
38576      * 
38577      */
38578     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38579
38580     /**
38581      * compile the template
38582      *
38583      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38584      *
38585      */
38586     compile: function()
38587     {
38588         var s = this.html;
38589      
38590         s = ['<tpl>', s, '</tpl>'].join('');
38591     
38592         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38593             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38594             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38595             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38596             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38597             m,
38598             id     = 0,
38599             tpls   = [];
38600     
38601         while(true == !!(m = s.match(re))){
38602             var forMatch   = m[0].match(nameRe),
38603                 ifMatch   = m[0].match(ifRe),
38604                 execMatch   = m[0].match(execRe),
38605                 namedMatch   = m[0].match(namedRe),
38606                 
38607                 exp  = null, 
38608                 fn   = null,
38609                 exec = null,
38610                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38611                 
38612             if (ifMatch) {
38613                 // if - puts fn into test..
38614                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38615                 if(exp){
38616                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38617                 }
38618             }
38619             
38620             if (execMatch) {
38621                 // exec - calls a function... returns empty if true is  returned.
38622                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38623                 if(exp){
38624                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38625                 }
38626             }
38627             
38628             
38629             if (name) {
38630                 // for = 
38631                 switch(name){
38632                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38633                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38634                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38635                 }
38636             }
38637             var uid = namedMatch ? namedMatch[1] : id;
38638             
38639             
38640             tpls.push({
38641                 id:     namedMatch ? namedMatch[1] : id,
38642                 target: name,
38643                 exec:   exec,
38644                 test:   fn,
38645                 body:   m[1] || ''
38646             });
38647             if (namedMatch) {
38648                 s = s.replace(m[0], '');
38649             } else { 
38650                 s = s.replace(m[0], '{xtpl'+ id + '}');
38651             }
38652             ++id;
38653         }
38654         this.tpls = [];
38655         for(var i = tpls.length-1; i >= 0; --i){
38656             this.compileTpl(tpls[i]);
38657             this.tpls[tpls[i].id] = tpls[i];
38658         }
38659         this.master = tpls[tpls.length-1];
38660         return this;
38661     },
38662     /**
38663      * same as applyTemplate, except it's done to one of the subTemplates
38664      * when using named templates, you can do:
38665      *
38666      * var str = pl.applySubTemplate('your-name', values);
38667      *
38668      * 
38669      * @param {Number} id of the template
38670      * @param {Object} values to apply to template
38671      * @param {Object} parent (normaly the instance of this object)
38672      */
38673     applySubTemplate : function(id, values, parent)
38674     {
38675         
38676         
38677         var t = this.tpls[id];
38678         
38679         
38680         try { 
38681             if(t.test && !t.test.call(this, values, parent)){
38682                 return '';
38683             }
38684         } catch(e) {
38685             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38686             Roo.log(e.toString());
38687             Roo.log(t.test);
38688             return ''
38689         }
38690         try { 
38691             
38692             if(t.exec && t.exec.call(this, values, parent)){
38693                 return '';
38694             }
38695         } catch(e) {
38696             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38697             Roo.log(e.toString());
38698             Roo.log(t.exec);
38699             return ''
38700         }
38701         try {
38702             var vs = t.target ? t.target.call(this, values, parent) : values;
38703             parent = t.target ? values : parent;
38704             if(t.target && vs instanceof Array){
38705                 var buf = [];
38706                 for(var i = 0, len = vs.length; i < len; i++){
38707                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38708                 }
38709                 return buf.join('');
38710             }
38711             return t.compiled.call(this, vs, parent);
38712         } catch (e) {
38713             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38714             Roo.log(e.toString());
38715             Roo.log(t.compiled);
38716             return '';
38717         }
38718     },
38719
38720     compileTpl : function(tpl)
38721     {
38722         var fm = Roo.util.Format;
38723         var useF = this.disableFormats !== true;
38724         var sep = Roo.isGecko ? "+" : ",";
38725         var undef = function(str) {
38726             Roo.log("Property not found :"  + str);
38727             return '';
38728         };
38729         
38730         var fn = function(m, name, format, args)
38731         {
38732             //Roo.log(arguments);
38733             args = args ? args.replace(/\\'/g,"'") : args;
38734             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38735             if (typeof(format) == 'undefined') {
38736                 format= 'htmlEncode';
38737             }
38738             if (format == 'raw' ) {
38739                 format = false;
38740             }
38741             
38742             if(name.substr(0, 4) == 'xtpl'){
38743                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38744             }
38745             
38746             // build an array of options to determine if value is undefined..
38747             
38748             // basically get 'xxxx.yyyy' then do
38749             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38750             //    (function () { Roo.log("Property not found"); return ''; })() :
38751             //    ......
38752             
38753             var udef_ar = [];
38754             var lookfor = '';
38755             Roo.each(name.split('.'), function(st) {
38756                 lookfor += (lookfor.length ? '.': '') + st;
38757                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38758             });
38759             
38760             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38761             
38762             
38763             if(format && useF){
38764                 
38765                 args = args ? ',' + args : "";
38766                  
38767                 if(format.substr(0, 5) != "this."){
38768                     format = "fm." + format + '(';
38769                 }else{
38770                     format = 'this.call("'+ format.substr(5) + '", ';
38771                     args = ", values";
38772                 }
38773                 
38774                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38775             }
38776              
38777             if (args.length) {
38778                 // called with xxyx.yuu:(test,test)
38779                 // change to ()
38780                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38781             }
38782             // raw.. - :raw modifier..
38783             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38784             
38785         };
38786         var body;
38787         // branched to use + in gecko and [].join() in others
38788         if(Roo.isGecko){
38789             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38790                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38791                     "';};};";
38792         }else{
38793             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38794             body.push(tpl.body.replace(/(\r\n|\n)/g,
38795                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38796             body.push("'].join('');};};");
38797             body = body.join('');
38798         }
38799         
38800         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38801        
38802         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38803         eval(body);
38804         
38805         return this;
38806     },
38807
38808     applyTemplate : function(values){
38809         return this.master.compiled.call(this, values, {});
38810         //var s = this.subs;
38811     },
38812
38813     apply : function(){
38814         return this.applyTemplate.apply(this, arguments);
38815     }
38816
38817  });
38818
38819 Roo.XTemplate.from = function(el){
38820     el = Roo.getDom(el);
38821     return new Roo.XTemplate(el.value || el.innerHTML);
38822 };