roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @static
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 [required] 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.DataReader} reader [required]  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          
716         if(!o){
717             if(success !== false){
718                 this.fireEvent("load", this, [], options, o);
719             }
720             if(options.callback){
721                 options.callback.call(options.scope || this, [], options, false);
722             }
723             return;
724         }
725         // if data returned failure - throw an exception.
726         if (o.success === false) {
727             // show a message if no listener is registered.
728             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
729                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730             }
731             // loadmask wil be hooked into this..
732             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
733             return;
734         }
735         var r = o.records, t = o.totalRecords || r.length;
736         
737         this.fireEvent("beforeloadadd", this, r, options, o);
738         
739         if(!options || options.add !== true){
740             if(this.pruneModifiedRecords){
741                 this.modified = [];
742             }
743             for(var i = 0, len = r.length; i < len; i++){
744                 r[i].join(this);
745             }
746             if(this.snapshot){
747                 this.data = this.snapshot;
748                 delete this.snapshot;
749             }
750             this.data.clear();
751             this.data.addAll(r);
752             this.totalLength = t;
753             this.applySort();
754             this.fireEvent("datachanged", this);
755         }else{
756             this.totalLength = Math.max(t, this.data.length+r.length);
757             this.add(r);
758         }
759         
760         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761                 
762             var e = new Roo.data.Record({});
763
764             e.set(this.parent.displayField, this.parent.emptyTitle);
765             e.set(this.parent.valueField, '');
766
767             this.insert(0, e);
768         }
769             
770         this.fireEvent("load", this, r, options, o);
771         if(options.callback){
772             options.callback.call(options.scope || this, r, options, true);
773         }
774     },
775
776
777     /**
778      * Loads data from a passed data block. A Reader which understands the format of the data
779      * must have been configured in the constructor.
780      * @param {Object} data The data block from which to read the Records.  The format of the data expected
781      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
782      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783      */
784     loadData : function(o, append){
785         var r = this.reader.readRecords(o);
786         this.loadRecords(r, {add: append}, true);
787     },
788     
789      /**
790      * using 'cn' the nested child reader read the child array into it's child stores.
791      * @param {Object} rec The record with a 'children array
792      */
793     loadDataFromChildren : function(rec)
794     {
795         this.loadData(this.reader.toLoadData(rec));
796     },
797     
798
799     /**
800      * Gets the number of cached records.
801      * <p>
802      * <em>If using paging, this may not be the total size of the dataset. If the data object
803      * used by the Reader contains the dataset size, then the getTotalCount() function returns
804      * the data set size</em>
805      */
806     getCount : function(){
807         return this.data.length || 0;
808     },
809
810     /**
811      * Gets the total number of records in the dataset as returned by the server.
812      * <p>
813      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
814      * the dataset size</em>
815      */
816     getTotalCount : function(){
817         return this.totalLength || 0;
818     },
819
820     /**
821      * Returns the sort state of the Store as an object with two properties:
822      * <pre><code>
823  field {String} The name of the field by which the Records are sorted
824  direction {String} The sort order, "ASC" or "DESC"
825      * </code></pre>
826      */
827     getSortState : function(){
828         return this.sortInfo;
829     },
830
831     // private
832     applySort : function(){
833         if(this.sortInfo && !this.remoteSort){
834             var s = this.sortInfo, f = s.field;
835             var st = this.fields.get(f).sortType;
836             var fn = function(r1, r2){
837                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
838                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
839             };
840             this.data.sort(s.direction, fn);
841             if(this.snapshot && this.snapshot != this.data){
842                 this.snapshot.sort(s.direction, fn);
843             }
844         }
845     },
846
847     /**
848      * Sets the default sort column and order to be used by the next load operation.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     setDefaultSort : function(field, dir){
853         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
854     },
855
856     /**
857      * Sort the Records.
858      * If remote sorting is used, the sort is performed on the server, and the cache is
859      * reloaded. If local sorting is used, the cache is sorted internally.
860      * @param {String} fieldName The name of the field to sort by.
861      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862      */
863     sort : function(fieldName, dir){
864         var f = this.fields.get(fieldName);
865         if(!dir){
866             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
867             
868             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
869                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
870             }else{
871                 dir = f.sortDir;
872             }
873         }
874         this.sortToggle[f.name] = dir;
875         this.sortInfo = {field: f.name, direction: dir};
876         if(!this.remoteSort){
877             this.applySort();
878             this.fireEvent("datachanged", this);
879         }else{
880             this.load(this.lastOptions);
881         }
882     },
883
884     /**
885      * Calls the specified function for each of the Records in the cache.
886      * @param {Function} fn The function to call. The Record is passed as the first parameter.
887      * Returning <em>false</em> aborts and exits the iteration.
888      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
889      */
890     each : function(fn, scope){
891         this.data.each(fn, scope);
892     },
893
894     /**
895      * Gets all records modified since the last commit.  Modified records are persisted across load operations
896      * (e.g., during paging).
897      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
898      */
899     getModifiedRecords : function(){
900         return this.modified;
901     },
902
903     // private
904     createFilterFn : function(property, value, anyMatch){
905         if(!value.exec){ // not a regex
906             value = String(value);
907             if(value.length == 0){
908                 return false;
909             }
910             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
911         }
912         return function(r){
913             return value.test(r.data[property]);
914         };
915     },
916
917     /**
918      * Sums the value of <i>property</i> for each record between start and end and returns the result.
919      * @param {String} property A field on your records
920      * @param {Number} start The record index to start at (defaults to 0)
921      * @param {Number} end The last record index to include (defaults to length - 1)
922      * @return {Number} The sum
923      */
924     sum : function(property, start, end){
925         var rs = this.data.items, v = 0;
926         start = start || 0;
927         end = (end || end === 0) ? end : rs.length-1;
928
929         for(var i = start; i <= end; i++){
930             v += (rs[i].data[property] || 0);
931         }
932         return v;
933     },
934
935     /**
936      * Filter the records by a specified property.
937      * @param {String} field A field on your records
938      * @param {String/RegExp} value Either a string that the field
939      * should start with or a RegExp to test against the field
940      * @param {Boolean} anyMatch True to match any part not just the beginning
941      */
942     filter : function(property, value, anyMatch){
943         var fn = this.createFilterFn(property, value, anyMatch);
944         return fn ? this.filterBy(fn) : this.clearFilter();
945     },
946
947     /**
948      * Filter by a function. The specified function will be called with each
949      * record in this data source. If the function returns true the record is included,
950      * otherwise it is filtered.
951      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
952      * @param {Object} scope (optional) The scope of the function (defaults to this)
953      */
954     filterBy : function(fn, scope){
955         this.snapshot = this.snapshot || this.data;
956         this.data = this.queryBy(fn, scope||this);
957         this.fireEvent("datachanged", this);
958     },
959
960     /**
961      * Query the records by a specified property.
962      * @param {String} field A field on your records
963      * @param {String/RegExp} value Either a string that the field
964      * should start with or a RegExp to test against the field
965      * @param {Boolean} anyMatch True to match any part not just the beginning
966      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
967      */
968     query : function(property, value, anyMatch){
969         var fn = this.createFilterFn(property, value, anyMatch);
970         return fn ? this.queryBy(fn) : this.data.clone();
971     },
972
973     /**
974      * Query by a function. The specified function will be called with each
975      * record in this data source. If the function returns true the record is included
976      * in the results.
977      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
978      * @param {Object} scope (optional) The scope of the function (defaults to this)
979       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
980      **/
981     queryBy : function(fn, scope){
982         var data = this.snapshot || this.data;
983         return data.filterBy(fn, scope||this);
984     },
985
986     /**
987      * Collects unique values for a particular dataIndex from this store.
988      * @param {String} dataIndex The property to collect
989      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
990      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
991      * @return {Array} An array of the unique values
992      **/
993     collect : function(dataIndex, allowNull, bypassFilter){
994         var d = (bypassFilter === true && this.snapshot) ?
995                 this.snapshot.items : this.data.items;
996         var v, sv, r = [], l = {};
997         for(var i = 0, len = d.length; i < len; i++){
998             v = d[i].data[dataIndex];
999             sv = String(v);
1000             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1001                 l[sv] = true;
1002                 r[r.length] = v;
1003             }
1004         }
1005         return r;
1006     },
1007
1008     /**
1009      * Revert to a view of the Record cache with no filtering applied.
1010      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1011      */
1012     clearFilter : function(suppressEvent){
1013         if(this.snapshot && this.snapshot != this.data){
1014             this.data = this.snapshot;
1015             delete this.snapshot;
1016             if(suppressEvent !== true){
1017                 this.fireEvent("datachanged", this);
1018             }
1019         }
1020     },
1021
1022     // private
1023     afterEdit : function(record){
1024         if(this.modified.indexOf(record) == -1){
1025             this.modified.push(record);
1026         }
1027         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1028     },
1029     
1030     // private
1031     afterReject : function(record){
1032         this.modified.remove(record);
1033         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1034     },
1035
1036     // private
1037     afterCommit : function(record){
1038         this.modified.remove(record);
1039         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1040     },
1041
1042     /**
1043      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1044      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1045      */
1046     commitChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].commit();
1051         }
1052     },
1053
1054     /**
1055      * Cancel outstanding changes on all changed records.
1056      */
1057     rejectChanges : function(){
1058         var m = this.modified.slice(0);
1059         this.modified = [];
1060         for(var i = 0, len = m.length; i < len; i++){
1061             m[i].reject();
1062         }
1063     },
1064
1065     onMetaChange : function(meta, rtype, o){
1066         this.recordType = rtype;
1067         this.fields = rtype.prototype.fields;
1068         delete this.snapshot;
1069         this.sortInfo = meta.sortInfo || this.sortInfo;
1070         this.modified = [];
1071         this.fireEvent('metachange', this, this.reader.meta);
1072     },
1073     
1074     moveIndex : function(data, type)
1075     {
1076         var index = this.indexOf(data);
1077         
1078         var newIndex = index + type;
1079         
1080         this.remove(data);
1081         
1082         this.insert(newIndex, data);
1083         
1084     }
1085 });/*
1086  * Based on:
1087  * Ext JS Library 1.1.1
1088  * Copyright(c) 2006-2007, Ext JS, LLC.
1089  *
1090  * Originally Released Under LGPL - original licence link has changed is not relivant.
1091  *
1092  * Fork - LGPL
1093  * <script type="text/javascript">
1094  */
1095
1096 /**
1097  * @class Roo.data.SimpleStore
1098  * @extends Roo.data.Store
1099  * Small helper class to make creating Stores from Array data easier.
1100  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1101  * @cfg {Array} fields An array of field definition objects, or field name strings.
1102  * @cfg {Object} an existing reader (eg. copied from another store)
1103  * @cfg {Array} data The multi-dimensional array of data
1104  * @cfg {Roo.data.DataProxy} proxy [not-required]  
1105  * @cfg {Roo.data.Reader} reader  [not-required] 
1106  * @constructor
1107  * @param {Object} config
1108  */
1109 Roo.data.SimpleStore = function(config)
1110 {
1111     Roo.data.SimpleStore.superclass.constructor.call(this, {
1112         isLocal : true,
1113         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1114                 id: config.id
1115             },
1116             Roo.data.Record.create(config.fields)
1117         ),
1118         proxy : new Roo.data.MemoryProxy(config.data)
1119     });
1120     this.load();
1121 };
1122 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1123  * Based on:
1124  * Ext JS Library 1.1.1
1125  * Copyright(c) 2006-2007, Ext JS, LLC.
1126  *
1127  * Originally Released Under LGPL - original licence link has changed is not relivant.
1128  *
1129  * Fork - LGPL
1130  * <script type="text/javascript">
1131  */
1132
1133 /**
1134 /**
1135  * @extends Roo.data.Store
1136  * @class Roo.data.JsonStore
1137  * Small helper class to make creating Stores for JSON data easier. <br/>
1138 <pre><code>
1139 var store = new Roo.data.JsonStore({
1140     url: 'get-images.php',
1141     root: 'images',
1142     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1143 });
1144 </code></pre>
1145  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1146  * JsonReader and HttpProxy (unless inline data is provided).</b>
1147  * @cfg {Array} fields An array of field definition objects, or field name strings.
1148  * @constructor
1149  * @param {Object} config
1150  */
1151 Roo.data.JsonStore = function(c){
1152     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1153         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1154         reader: new Roo.data.JsonReader(c, c.fields)
1155     }));
1156 };
1157 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1158  * Based on:
1159  * Ext JS Library 1.1.1
1160  * Copyright(c) 2006-2007, Ext JS, LLC.
1161  *
1162  * Originally Released Under LGPL - original licence link has changed is not relivant.
1163  *
1164  * Fork - LGPL
1165  * <script type="text/javascript">
1166  */
1167
1168  
1169 Roo.data.Field = function(config){
1170     if(typeof config == "string"){
1171         config = {name: config};
1172     }
1173     Roo.apply(this, config);
1174     
1175     if(!this.type){
1176         this.type = "auto";
1177     }
1178     
1179     var st = Roo.data.SortTypes;
1180     // named sortTypes are supported, here we look them up
1181     if(typeof this.sortType == "string"){
1182         this.sortType = st[this.sortType];
1183     }
1184     
1185     // set default sortType for strings and dates
1186     if(!this.sortType){
1187         switch(this.type){
1188             case "string":
1189                 this.sortType = st.asUCString;
1190                 break;
1191             case "date":
1192                 this.sortType = st.asDate;
1193                 break;
1194             default:
1195                 this.sortType = st.none;
1196         }
1197     }
1198
1199     // define once
1200     var stripRe = /[\$,%]/g;
1201
1202     // prebuilt conversion function for this field, instead of
1203     // switching every time we're reading a value
1204     if(!this.convert){
1205         var cv, dateFormat = this.dateFormat;
1206         switch(this.type){
1207             case "":
1208             case "auto":
1209             case undefined:
1210                 cv = function(v){ return v; };
1211                 break;
1212             case "string":
1213                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1214                 break;
1215             case "int":
1216                 cv = function(v){
1217                     return v !== undefined && v !== null && v !== '' ?
1218                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1219                     };
1220                 break;
1221             case "float":
1222                 cv = function(v){
1223                     return v !== undefined && v !== null && v !== '' ?
1224                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1225                     };
1226                 break;
1227             case "bool":
1228             case "boolean":
1229                 cv = function(v){ return v === true || v === "true" || v == 1; };
1230                 break;
1231             case "date":
1232                 cv = function(v){
1233                     if(!v){
1234                         return '';
1235                     }
1236                     if(v instanceof Date){
1237                         return v;
1238                     }
1239                     if(dateFormat){
1240                         if(dateFormat == "timestamp"){
1241                             return new Date(v*1000);
1242                         }
1243                         return Date.parseDate(v, dateFormat);
1244                     }
1245                     var parsed = Date.parse(v);
1246                     return parsed ? new Date(parsed) : null;
1247                 };
1248              break;
1249             
1250         }
1251         this.convert = cv;
1252     }
1253 };
1254
1255 Roo.data.Field.prototype = {
1256     dateFormat: null,
1257     defaultValue: "",
1258     mapping: null,
1259     sortType : null,
1260     sortDir : "ASC"
1261 };/*
1262  * Based on:
1263  * Ext JS Library 1.1.1
1264  * Copyright(c) 2006-2007, Ext JS, LLC.
1265  *
1266  * Originally Released Under LGPL - original licence link has changed is not relivant.
1267  *
1268  * Fork - LGPL
1269  * <script type="text/javascript">
1270  */
1271  
1272 // Base class for reading structured data from a data source.  This class is intended to be
1273 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1274
1275 /**
1276  * @class Roo.data.DataReader
1277  * @abstract
1278  * Base class for reading structured data from a data source.  This class is intended to be
1279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1280  */
1281
1282 Roo.data.DataReader = function(meta, recordType){
1283     
1284     this.meta = meta;
1285     
1286     this.recordType = recordType instanceof Array ? 
1287         Roo.data.Record.create(recordType) : recordType;
1288 };
1289
1290 Roo.data.DataReader.prototype = {
1291     
1292     
1293     readerType : 'Data',
1294      /**
1295      * Create an empty record
1296      * @param {Object} data (optional) - overlay some values
1297      * @return {Roo.data.Record} record created.
1298      */
1299     newRow :  function(d) {
1300         var da =  {};
1301         this.recordType.prototype.fields.each(function(c) {
1302             switch( c.type) {
1303                 case 'int' : da[c.name] = 0; break;
1304                 case 'date' : da[c.name] = new Date(); break;
1305                 case 'float' : da[c.name] = 0.0; break;
1306                 case 'boolean' : da[c.name] = false; break;
1307                 default : da[c.name] = ""; break;
1308             }
1309             
1310         });
1311         return new this.recordType(Roo.apply(da, d));
1312     }
1313     
1314     
1315 };/*
1316  * Based on:
1317  * Ext JS Library 1.1.1
1318  * Copyright(c) 2006-2007, Ext JS, LLC.
1319  *
1320  * Originally Released Under LGPL - original licence link has changed is not relivant.
1321  *
1322  * Fork - LGPL
1323  * <script type="text/javascript">
1324  */
1325
1326 /**
1327  * @class Roo.data.DataProxy
1328  * @extends Roo.util.Observable
1329  * @abstract
1330  * This class is an abstract base class for implementations which provide retrieval of
1331  * unformatted data objects.<br>
1332  * <p>
1333  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1334  * (of the appropriate type which knows how to parse the data object) to provide a block of
1335  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1336  * <p>
1337  * Custom implementations must implement the load method as described in
1338  * {@link Roo.data.HttpProxy#load}.
1339  */
1340 Roo.data.DataProxy = function(){
1341     this.addEvents({
1342         /**
1343          * @event beforeload
1344          * Fires before a network request is made to retrieve a data object.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} params The params parameter to the load function.
1347          */
1348         beforeload : true,
1349         /**
1350          * @event load
1351          * Fires before the load method's callback is called.
1352          * @param {Object} This DataProxy object.
1353          * @param {Object} o The data object.
1354          * @param {Object} arg The callback argument object passed to the load function.
1355          */
1356         load : true,
1357         /**
1358          * @event loadexception
1359          * Fires if an Exception occurs during data retrieval.
1360          * @param {Object} This DataProxy object.
1361          * @param {Object} o The data object.
1362          * @param {Object} arg The callback argument object passed to the load function.
1363          * @param {Object} e The Exception.
1364          */
1365         loadexception : true
1366     });
1367     Roo.data.DataProxy.superclass.constructor.call(this);
1368 };
1369
1370 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1371
1372     /**
1373      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1374      */
1375 /*
1376  * Based on:
1377  * Ext JS Library 1.1.1
1378  * Copyright(c) 2006-2007, Ext JS, LLC.
1379  *
1380  * Originally Released Under LGPL - original licence link has changed is not relivant.
1381  *
1382  * Fork - LGPL
1383  * <script type="text/javascript">
1384  */
1385 /**
1386  * @class Roo.data.MemoryProxy
1387  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1388  * to the Reader when its load method is called.
1389  * @constructor
1390  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1391  */
1392 Roo.data.MemoryProxy = function(data){
1393     if (data.data) {
1394         data = data.data;
1395     }
1396     Roo.data.MemoryProxy.superclass.constructor.call(this);
1397     this.data = data;
1398 };
1399
1400 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1401     
1402     /**
1403      * Load data from the requested source (in this case an in-memory
1404      * data object passed to the constructor), read the data object into
1405      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1406      * process that block using the passed callback.
1407      * @param {Object} params This parameter is not used by the MemoryProxy class.
1408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1409      * object into a block of Roo.data.Records.
1410      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1411      * The function must be passed <ul>
1412      * <li>The Record block object</li>
1413      * <li>The "arg" argument from the load function</li>
1414      * <li>A boolean success indicator</li>
1415      * </ul>
1416      * @param {Object} scope The scope in which to call the callback
1417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1418      */
1419     load : function(params, reader, callback, scope, arg){
1420         params = params || {};
1421         var result;
1422         try {
1423             result = reader.readRecords(params.data ? params.data :this.data);
1424         }catch(e){
1425             this.fireEvent("loadexception", this, arg, null, e);
1426             callback.call(scope, null, arg, false);
1427             return;
1428         }
1429         callback.call(scope, result, arg, true);
1430     },
1431     
1432     // private
1433     update : function(params, records){
1434         
1435     }
1436 });/*
1437  * Based on:
1438  * Ext JS Library 1.1.1
1439  * Copyright(c) 2006-2007, Ext JS, LLC.
1440  *
1441  * Originally Released Under LGPL - original licence link has changed is not relivant.
1442  *
1443  * Fork - LGPL
1444  * <script type="text/javascript">
1445  */
1446 /**
1447  * @class Roo.data.HttpProxy
1448  * @extends Roo.data.DataProxy
1449  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1450  * configured to reference a certain URL.<br><br>
1451  * <p>
1452  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1453  * from which the running page was served.<br><br>
1454  * <p>
1455  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1456  * <p>
1457  * Be aware that to enable the browser to parse an XML document, the server must set
1458  * the Content-Type header in the HTTP response to "text/xml".
1459  * @constructor
1460  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1461  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1462  * will be used to make the request.
1463  */
1464 Roo.data.HttpProxy = function(conn){
1465     Roo.data.HttpProxy.superclass.constructor.call(this);
1466     // is conn a conn config or a real conn?
1467     this.conn = conn;
1468     this.useAjax = !conn || !conn.events;
1469   
1470 };
1471
1472 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1473     // thse are take from connection...
1474     
1475     /**
1476      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1477      */
1478     /**
1479      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1480      * extra parameters to each request made by this object. (defaults to undefined)
1481      */
1482     /**
1483      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1484      *  to each request made by this object. (defaults to undefined)
1485      */
1486     /**
1487      * @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)
1488      */
1489     /**
1490      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1491      */
1492      /**
1493      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1494      * @type Boolean
1495      */
1496   
1497
1498     /**
1499      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1500      * @type Boolean
1501      */
1502     /**
1503      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1504      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1505      * a finer-grained basis than the DataProxy events.
1506      */
1507     getConnection : function(){
1508         return this.useAjax ? Roo.Ajax : this.conn;
1509     },
1510
1511     /**
1512      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1513      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1514      * process that block using the passed callback.
1515      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1516      * for the request to the remote server.
1517      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1518      * object into a block of Roo.data.Records.
1519      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1520      * The function must be passed <ul>
1521      * <li>The Record block object</li>
1522      * <li>The "arg" argument from the load function</li>
1523      * <li>A boolean success indicator</li>
1524      * </ul>
1525      * @param {Object} scope The scope in which to call the callback
1526      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1527      */
1528     load : function(params, reader, callback, scope, arg){
1529         if(this.fireEvent("beforeload", this, params) !== false){
1530             var  o = {
1531                 params : params || {},
1532                 request: {
1533                     callback : callback,
1534                     scope : scope,
1535                     arg : arg
1536                 },
1537                 reader: reader,
1538                 callback : this.loadResponse,
1539                 scope: this
1540             };
1541             if(this.useAjax){
1542                 Roo.applyIf(o, this.conn);
1543                 if(this.activeRequest){
1544                     Roo.Ajax.abort(this.activeRequest);
1545                 }
1546                 this.activeRequest = Roo.Ajax.request(o);
1547             }else{
1548                 this.conn.request(o);
1549             }
1550         }else{
1551             callback.call(scope||this, null, arg, false);
1552         }
1553     },
1554
1555     // private
1556     loadResponse : function(o, success, response){
1557         delete this.activeRequest;
1558         if(!success){
1559             this.fireEvent("loadexception", this, o, response);
1560             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1561             return;
1562         }
1563         var result;
1564         try {
1565             result = o.reader.read(response);
1566         }catch(e){
1567             o.success = false;
1568             o.raw = { errorMsg : response.responseText };
1569             this.fireEvent("loadexception", this, o, response, e);
1570             o.request.callback.call(o.request.scope, o, o.request.arg, false);
1571             return;
1572         }
1573         
1574         this.fireEvent("load", this, o, o.request.arg);
1575         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1576     },
1577
1578     // private
1579     update : function(dataSet){
1580
1581     },
1582
1583     // private
1584     updateResponse : function(dataSet){
1585
1586     }
1587 });/*
1588  * Based on:
1589  * Ext JS Library 1.1.1
1590  * Copyright(c) 2006-2007, Ext JS, LLC.
1591  *
1592  * Originally Released Under LGPL - original licence link has changed is not relivant.
1593  *
1594  * Fork - LGPL
1595  * <script type="text/javascript">
1596  */
1597
1598 /**
1599  * @class Roo.data.ScriptTagProxy
1600  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1601  * other than the originating domain of the running page.<br><br>
1602  * <p>
1603  * <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
1604  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1605  * <p>
1606  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1607  * source code that is used as the source inside a &lt;script> tag.<br><br>
1608  * <p>
1609  * In order for the browser to process the returned data, the server must wrap the data object
1610  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1611  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1612  * depending on whether the callback name was passed:
1613  * <p>
1614  * <pre><code>
1615 boolean scriptTag = false;
1616 String cb = request.getParameter("callback");
1617 if (cb != null) {
1618     scriptTag = true;
1619     response.setContentType("text/javascript");
1620 } else {
1621     response.setContentType("application/x-json");
1622 }
1623 Writer out = response.getWriter();
1624 if (scriptTag) {
1625     out.write(cb + "(");
1626 }
1627 out.print(dataBlock.toJsonString());
1628 if (scriptTag) {
1629     out.write(");");
1630 }
1631 </pre></code>
1632  *
1633  * @constructor
1634  * @param {Object} config A configuration object.
1635  */
1636 Roo.data.ScriptTagProxy = function(config){
1637     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1638     Roo.apply(this, config);
1639     this.head = document.getElementsByTagName("head")[0];
1640 };
1641
1642 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1643
1644 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1645     /**
1646      * @cfg {String} url The URL from which to request the data object.
1647      */
1648     /**
1649      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1650      */
1651     timeout : 30000,
1652     /**
1653      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1654      * the server the name of the callback function set up by the load call to process the returned data object.
1655      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1656      * javascript output which calls this named function passing the data object as its only parameter.
1657      */
1658     callbackParam : "callback",
1659     /**
1660      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1661      * name to the request.
1662      */
1663     nocache : true,
1664
1665     /**
1666      * Load data from the configured URL, read the data object into
1667      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1668      * process that block using the passed callback.
1669      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1670      * for the request to the remote server.
1671      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1672      * object into a block of Roo.data.Records.
1673      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1674      * The function must be passed <ul>
1675      * <li>The Record block object</li>
1676      * <li>The "arg" argument from the load function</li>
1677      * <li>A boolean success indicator</li>
1678      * </ul>
1679      * @param {Object} scope The scope in which to call the callback
1680      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1681      */
1682     load : function(params, reader, callback, scope, arg){
1683         if(this.fireEvent("beforeload", this, params) !== false){
1684
1685             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1686
1687             var url = this.url;
1688             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1689             if(this.nocache){
1690                 url += "&_dc=" + (new Date().getTime());
1691             }
1692             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1693             var trans = {
1694                 id : transId,
1695                 cb : "stcCallback"+transId,
1696                 scriptId : "stcScript"+transId,
1697                 params : params,
1698                 arg : arg,
1699                 url : url,
1700                 callback : callback,
1701                 scope : scope,
1702                 reader : reader
1703             };
1704             var conn = this;
1705
1706             window[trans.cb] = function(o){
1707                 conn.handleResponse(o, trans);
1708             };
1709
1710             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1711
1712             if(this.autoAbort !== false){
1713                 this.abort();
1714             }
1715
1716             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1717
1718             var script = document.createElement("script");
1719             script.setAttribute("src", url);
1720             script.setAttribute("type", "text/javascript");
1721             script.setAttribute("id", trans.scriptId);
1722             this.head.appendChild(script);
1723
1724             this.trans = trans;
1725         }else{
1726             callback.call(scope||this, null, arg, false);
1727         }
1728     },
1729
1730     // private
1731     isLoading : function(){
1732         return this.trans ? true : false;
1733     },
1734
1735     /**
1736      * Abort the current server request.
1737      */
1738     abort : function(){
1739         if(this.isLoading()){
1740             this.destroyTrans(this.trans);
1741         }
1742     },
1743
1744     // private
1745     destroyTrans : function(trans, isLoaded){
1746         this.head.removeChild(document.getElementById(trans.scriptId));
1747         clearTimeout(trans.timeoutId);
1748         if(isLoaded){
1749             window[trans.cb] = undefined;
1750             try{
1751                 delete window[trans.cb];
1752             }catch(e){}
1753         }else{
1754             // if hasn't been loaded, wait for load to remove it to prevent script error
1755             window[trans.cb] = function(){
1756                 window[trans.cb] = undefined;
1757                 try{
1758                     delete window[trans.cb];
1759                 }catch(e){}
1760             };
1761         }
1762     },
1763
1764     // private
1765     handleResponse : function(o, trans){
1766         this.trans = false;
1767         this.destroyTrans(trans, true);
1768         var result;
1769         try {
1770             result = trans.reader.readRecords(o);
1771         }catch(e){
1772             this.fireEvent("loadexception", this, o, trans.arg, e);
1773             trans.callback.call(trans.scope||window, null, trans.arg, false);
1774             return;
1775         }
1776         this.fireEvent("load", this, o, trans.arg);
1777         trans.callback.call(trans.scope||window, result, trans.arg, true);
1778     },
1779
1780     // private
1781     handleFailure : function(trans){
1782         this.trans = false;
1783         this.destroyTrans(trans, false);
1784         this.fireEvent("loadexception", this, null, trans.arg);
1785         trans.callback.call(trans.scope||window, null, trans.arg, false);
1786     }
1787 });/*
1788  * Based on:
1789  * Ext JS Library 1.1.1
1790  * Copyright(c) 2006-2007, Ext JS, LLC.
1791  *
1792  * Originally Released Under LGPL - original licence link has changed is not relivant.
1793  *
1794  * Fork - LGPL
1795  * <script type="text/javascript">
1796  */
1797
1798 /**
1799  * @class Roo.data.JsonReader
1800  * @extends Roo.data.DataReader
1801  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1802  * based on mappings in a provided Roo.data.Record constructor.
1803  * 
1804  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1805  * in the reply previously. 
1806  * 
1807  * <p>
1808  * Example code:
1809  * <pre><code>
1810 var RecordDef = Roo.data.Record.create([
1811     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1812     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1813 ]);
1814 var myReader = new Roo.data.JsonReader({
1815     totalProperty: "results",    // The property which contains the total dataset size (optional)
1816     root: "rows",                // The property which contains an Array of row objects
1817     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1818 }, RecordDef);
1819 </code></pre>
1820  * <p>
1821  * This would consume a JSON file like this:
1822  * <pre><code>
1823 { 'results': 2, 'rows': [
1824     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1825     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1826 }
1827 </code></pre>
1828  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1829  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1830  * paged from the remote server.
1831  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1832  * @cfg {String} root name of the property which contains the Array of row objects.
1833  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1834  * @cfg {Array} fields Array of field definition objects
1835  * @constructor
1836  * Create a new JsonReader
1837  * @param {Object} meta Metadata configuration options
1838  * @param {Object} recordType Either an Array of field definition objects,
1839  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1840  */
1841 Roo.data.JsonReader = function(meta, recordType){
1842     
1843     meta = meta || {};
1844     // set some defaults:
1845     Roo.applyIf(meta, {
1846         totalProperty: 'total',
1847         successProperty : 'success',
1848         root : 'data',
1849         id : 'id'
1850     });
1851     
1852     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1853 };
1854 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1855     
1856     readerType : 'Json',
1857     
1858     /**
1859      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1860      * Used by Store query builder to append _requestMeta to params.
1861      * 
1862      */
1863     metaFromRemote : false,
1864     /**
1865      * This method is only used by a DataProxy which has retrieved data from a remote server.
1866      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1867      * @return {Object} data A data block which is used by an Roo.data.Store object as
1868      * a cache of Roo.data.Records.
1869      */
1870     read : function(response){
1871         var json = response.responseText;
1872        
1873         var o = /* eval:var:o */ eval("("+json+")");
1874         if(!o) {
1875             throw {message: "JsonReader.read: Json object not found"};
1876         }
1877         
1878         if(o.metaData){
1879             
1880             delete this.ef;
1881             this.metaFromRemote = true;
1882             this.meta = o.metaData;
1883             this.recordType = Roo.data.Record.create(o.metaData.fields);
1884             this.onMetaChange(this.meta, this.recordType, o);
1885         }
1886         return this.readRecords(o);
1887     },
1888
1889     // private function a store will implement
1890     onMetaChange : function(meta, recordType, o){
1891
1892     },
1893
1894     /**
1895          * @ignore
1896          */
1897     simpleAccess: function(obj, subsc) {
1898         return obj[subsc];
1899     },
1900
1901         /**
1902          * @ignore
1903          */
1904     getJsonAccessor: function(){
1905         var re = /[\[\.]/;
1906         return function(expr) {
1907             try {
1908                 return(re.test(expr))
1909                     ? new Function("obj", "return obj." + expr)
1910                     : function(obj){
1911                         return obj[expr];
1912                     };
1913             } catch(e){}
1914             return Roo.emptyFn;
1915         };
1916     }(),
1917
1918     /**
1919      * Create a data block containing Roo.data.Records from an XML document.
1920      * @param {Object} o An object which contains an Array of row objects in the property specified
1921      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1922      * which contains the total size of the dataset.
1923      * @return {Object} data A data block which is used by an Roo.data.Store object as
1924      * a cache of Roo.data.Records.
1925      */
1926     readRecords : function(o){
1927         /**
1928          * After any data loads, the raw JSON data is available for further custom processing.
1929          * @type Object
1930          */
1931         this.o = o;
1932         var s = this.meta, Record = this.recordType,
1933             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1934
1935 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1936         if (!this.ef) {
1937             if(s.totalProperty) {
1938                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1939                 }
1940                 if(s.successProperty) {
1941                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1942                 }
1943                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1944                 if (s.id) {
1945                         var g = this.getJsonAccessor(s.id);
1946                         this.getId = function(rec) {
1947                                 var r = g(rec);  
1948                                 return (r === undefined || r === "") ? null : r;
1949                         };
1950                 } else {
1951                         this.getId = function(){return null;};
1952                 }
1953             this.ef = [];
1954             for(var jj = 0; jj < fl; jj++){
1955                 f = fi[jj];
1956                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1957                 this.ef[jj] = this.getJsonAccessor(map);
1958             }
1959         }
1960
1961         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1962         if(s.totalProperty){
1963             var vt = parseInt(this.getTotal(o), 10);
1964             if(!isNaN(vt)){
1965                 totalRecords = vt;
1966             }
1967         }
1968         if(s.successProperty){
1969             var vs = this.getSuccess(o);
1970             if(vs === false || vs === 'false'){
1971                 success = false;
1972             }
1973         }
1974         var records = [];
1975         for(var i = 0; i < c; i++){
1976             var n = root[i];
1977             var values = {};
1978             var id = this.getId(n);
1979             for(var j = 0; j < fl; j++){
1980                 f = fi[j];
1981                                 var v = this.ef[j](n);
1982                                 if (!f.convert) {
1983                                         Roo.log('missing convert for ' + f.name);
1984                                         Roo.log(f);
1985                                         continue;
1986                                 }
1987                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1988             }
1989                         if (!Record) {
1990                                 return {
1991                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
1992                                         success : false,
1993                                         records : [],
1994                                         totalRecords : 0
1995                                 };
1996                         }
1997             var record = new Record(values, id);
1998             record.json = n;
1999             records[i] = record;
2000         }
2001         return {
2002             raw : o,
2003             success : success,
2004             records : records,
2005             totalRecords : totalRecords
2006         };
2007     },
2008     // used when loading children.. @see loadDataFromChildren
2009     toLoadData: function(rec)
2010     {
2011         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2012         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2013         return { data : data, total : data.length };
2014         
2015     }
2016 });/*
2017  * Based on:
2018  * Ext JS Library 1.1.1
2019  * Copyright(c) 2006-2007, Ext JS, LLC.
2020  *
2021  * Originally Released Under LGPL - original licence link has changed is not relivant.
2022  *
2023  * Fork - LGPL
2024  * <script type="text/javascript">
2025  */
2026
2027 /**
2028  * @class Roo.data.XmlReader
2029  * @extends Roo.data.DataReader
2030  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2031  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2032  * <p>
2033  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2034  * header in the HTTP response must be set to "text/xml".</em>
2035  * <p>
2036  * Example code:
2037  * <pre><code>
2038 var RecordDef = Roo.data.Record.create([
2039    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2040    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2041 ]);
2042 var myReader = new Roo.data.XmlReader({
2043    totalRecords: "results", // The element which contains the total dataset size (optional)
2044    record: "row",           // The repeated element which contains row information
2045    id: "id"                 // The element within the row that provides an ID for the record (optional)
2046 }, RecordDef);
2047 </code></pre>
2048  * <p>
2049  * This would consume an XML file like this:
2050  * <pre><code>
2051 &lt;?xml?>
2052 &lt;dataset>
2053  &lt;results>2&lt;/results>
2054  &lt;row>
2055    &lt;id>1&lt;/id>
2056    &lt;name>Bill&lt;/name>
2057    &lt;occupation>Gardener&lt;/occupation>
2058  &lt;/row>
2059  &lt;row>
2060    &lt;id>2&lt;/id>
2061    &lt;name>Ben&lt;/name>
2062    &lt;occupation>Horticulturalist&lt;/occupation>
2063  &lt;/row>
2064 &lt;/dataset>
2065 </code></pre>
2066  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2067  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2068  * paged from the remote server.
2069  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2070  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2071  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2072  * a record identifier value.
2073  * @constructor
2074  * Create a new XmlReader
2075  * @param {Object} meta Metadata configuration options
2076  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2077  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2078  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2079  */
2080 Roo.data.XmlReader = function(meta, recordType){
2081     meta = meta || {};
2082     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2083 };
2084 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2085     
2086     readerType : 'Xml',
2087     
2088     /**
2089      * This method is only used by a DataProxy which has retrieved data from a remote server.
2090          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2091          * to contain a method called 'responseXML' that returns an XML document object.
2092      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2093      * a cache of Roo.data.Records.
2094      */
2095     read : function(response){
2096         var doc = response.responseXML;
2097         if(!doc) {
2098             throw {message: "XmlReader.read: XML Document not available"};
2099         }
2100         return this.readRecords(doc);
2101     },
2102
2103     /**
2104      * Create a data block containing Roo.data.Records from an XML document.
2105          * @param {Object} doc A parsed XML document.
2106      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2107      * a cache of Roo.data.Records.
2108      */
2109     readRecords : function(doc){
2110         /**
2111          * After any data loads/reads, the raw XML Document is available for further custom processing.
2112          * @type XMLDocument
2113          */
2114         this.xmlData = doc;
2115         var root = doc.documentElement || doc;
2116         var q = Roo.DomQuery;
2117         var recordType = this.recordType, fields = recordType.prototype.fields;
2118         var sid = this.meta.id;
2119         var totalRecords = 0, success = true;
2120         if(this.meta.totalRecords){
2121             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2122         }
2123         
2124         if(this.meta.success){
2125             var sv = q.selectValue(this.meta.success, root, true);
2126             success = sv !== false && sv !== 'false';
2127         }
2128         var records = [];
2129         var ns = q.select(this.meta.record, root);
2130         for(var i = 0, len = ns.length; i < len; i++) {
2131                 var n = ns[i];
2132                 var values = {};
2133                 var id = sid ? q.selectValue(sid, n) : undefined;
2134                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2135                     var f = fields.items[j];
2136                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2137                     v = f.convert(v);
2138                     values[f.name] = v;
2139                 }
2140                 var record = new recordType(values, id);
2141                 record.node = n;
2142                 records[records.length] = record;
2143             }
2144
2145             return {
2146                 success : success,
2147                 records : records,
2148                 totalRecords : totalRecords || records.length
2149             };
2150     }
2151 });/*
2152  * Based on:
2153  * Ext JS Library 1.1.1
2154  * Copyright(c) 2006-2007, Ext JS, LLC.
2155  *
2156  * Originally Released Under LGPL - original licence link has changed is not relivant.
2157  *
2158  * Fork - LGPL
2159  * <script type="text/javascript">
2160  */
2161
2162 /**
2163  * @class Roo.data.ArrayReader
2164  * @extends Roo.data.DataReader
2165  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2166  * Each element of that Array represents a row of data fields. The
2167  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2168  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2169  * <p>
2170  * Example code:.
2171  * <pre><code>
2172 var RecordDef = Roo.data.Record.create([
2173     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2174     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2175 ]);
2176 var myReader = new Roo.data.ArrayReader({
2177     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2178 }, RecordDef);
2179 </code></pre>
2180  * <p>
2181  * This would consume an Array like this:
2182  * <pre><code>
2183 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2184   </code></pre>
2185  
2186  * @constructor
2187  * Create a new JsonReader
2188  * @param {Object} meta Metadata configuration options.
2189  * @param {Object|Array} recordType Either an Array of field definition objects
2190  * 
2191  * @cfg {Array} fields Array of field definition objects
2192  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2193  * as specified to {@link Roo.data.Record#create},
2194  * or an {@link Roo.data.Record} object
2195  *
2196  * 
2197  * created using {@link Roo.data.Record#create}.
2198  */
2199 Roo.data.ArrayReader = function(meta, recordType)
2200 {    
2201     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2202 };
2203
2204 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2205     
2206       /**
2207      * Create a data block containing Roo.data.Records from an XML document.
2208      * @param {Object} o An Array of row objects which represents the dataset.
2209      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2210      * a cache of Roo.data.Records.
2211      */
2212     readRecords : function(o)
2213     {
2214         var sid = this.meta ? this.meta.id : null;
2215         var recordType = this.recordType, fields = recordType.prototype.fields;
2216         var records = [];
2217         var root = o;
2218         for(var i = 0; i < root.length; i++){
2219             var n = root[i];
2220             var values = {};
2221             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2222             for(var j = 0, jlen = fields.length; j < jlen; j++){
2223                 var f = fields.items[j];
2224                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2225                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2226                 v = f.convert(v);
2227                 values[f.name] = v;
2228             }
2229             var record = new recordType(values, id);
2230             record.json = n;
2231             records[records.length] = record;
2232         }
2233         return {
2234             records : records,
2235             totalRecords : records.length
2236         };
2237     },
2238     // used when loading children.. @see loadDataFromChildren
2239     toLoadData: function(rec)
2240     {
2241         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2242         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2243         
2244     }
2245     
2246     
2247 });/*
2248  * Based on:
2249  * Ext JS Library 1.1.1
2250  * Copyright(c) 2006-2007, Ext JS, LLC.
2251  *
2252  * Originally Released Under LGPL - original licence link has changed is not relivant.
2253  *
2254  * Fork - LGPL
2255  * <script type="text/javascript">
2256  */
2257
2258
2259 /**
2260  * @class Roo.data.Tree
2261  * @extends Roo.util.Observable
2262  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2263  * in the tree have most standard DOM functionality.
2264  * @constructor
2265  * @param {Node} root (optional) The root node
2266  */
2267 Roo.data.Tree = function(root){
2268    this.nodeHash = {};
2269    /**
2270     * The root node for this tree
2271     * @type Node
2272     */
2273    this.root = null;
2274    if(root){
2275        this.setRootNode(root);
2276    }
2277    this.addEvents({
2278        /**
2279         * @event append
2280         * Fires when a new child node is appended to a node in this tree.
2281         * @param {Tree} tree The owner tree
2282         * @param {Node} parent The parent node
2283         * @param {Node} node The newly appended node
2284         * @param {Number} index The index of the newly appended node
2285         */
2286        "append" : true,
2287        /**
2288         * @event remove
2289         * Fires when a child node is removed from a node in this tree.
2290         * @param {Tree} tree The owner tree
2291         * @param {Node} parent The parent node
2292         * @param {Node} node The child node removed
2293         */
2294        "remove" : true,
2295        /**
2296         * @event move
2297         * Fires when a node is moved to a new location in the tree
2298         * @param {Tree} tree The owner tree
2299         * @param {Node} node The node moved
2300         * @param {Node} oldParent The old parent of this node
2301         * @param {Node} newParent The new parent of this node
2302         * @param {Number} index The index it was moved to
2303         */
2304        "move" : true,
2305        /**
2306         * @event insert
2307         * Fires when a new child node is inserted in a node in this tree.
2308         * @param {Tree} tree The owner tree
2309         * @param {Node} parent The parent node
2310         * @param {Node} node The child node inserted
2311         * @param {Node} refNode The child node the node was inserted before
2312         */
2313        "insert" : true,
2314        /**
2315         * @event beforeappend
2316         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2317         * @param {Tree} tree The owner tree
2318         * @param {Node} parent The parent node
2319         * @param {Node} node The child node to be appended
2320         */
2321        "beforeappend" : true,
2322        /**
2323         * @event beforeremove
2324         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2325         * @param {Tree} tree The owner tree
2326         * @param {Node} parent The parent node
2327         * @param {Node} node The child node to be removed
2328         */
2329        "beforeremove" : true,
2330        /**
2331         * @event beforemove
2332         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2333         * @param {Tree} tree The owner tree
2334         * @param {Node} node The node being moved
2335         * @param {Node} oldParent The parent of the node
2336         * @param {Node} newParent The new parent the node is moving to
2337         * @param {Number} index The index it is being moved to
2338         */
2339        "beforemove" : true,
2340        /**
2341         * @event beforeinsert
2342         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2343         * @param {Tree} tree The owner tree
2344         * @param {Node} parent The parent node
2345         * @param {Node} node The child node to be inserted
2346         * @param {Node} refNode The child node the node is being inserted before
2347         */
2348        "beforeinsert" : true
2349    });
2350
2351     Roo.data.Tree.superclass.constructor.call(this);
2352 };
2353
2354 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2355     pathSeparator: "/",
2356
2357     proxyNodeEvent : function(){
2358         return this.fireEvent.apply(this, arguments);
2359     },
2360
2361     /**
2362      * Returns the root node for this tree.
2363      * @return {Node}
2364      */
2365     getRootNode : function(){
2366         return this.root;
2367     },
2368
2369     /**
2370      * Sets the root node for this tree.
2371      * @param {Node} node
2372      * @return {Node}
2373      */
2374     setRootNode : function(node){
2375         this.root = node;
2376         node.ownerTree = this;
2377         node.isRoot = true;
2378         this.registerNode(node);
2379         return node;
2380     },
2381
2382     /**
2383      * Gets a node in this tree by its id.
2384      * @param {String} id
2385      * @return {Node}
2386      */
2387     getNodeById : function(id){
2388         return this.nodeHash[id];
2389     },
2390
2391     registerNode : function(node){
2392         this.nodeHash[node.id] = node;
2393     },
2394
2395     unregisterNode : function(node){
2396         delete this.nodeHash[node.id];
2397     },
2398
2399     toString : function(){
2400         return "[Tree"+(this.id?" "+this.id:"")+"]";
2401     }
2402 });
2403
2404 /**
2405  * @class Roo.data.Node
2406  * @extends Roo.util.Observable
2407  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2408  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2409  * @constructor
2410  * @param {Object} attributes The attributes/config for the node
2411  */
2412 Roo.data.Node = function(attributes){
2413     /**
2414      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2415      * @type {Object}
2416      */
2417     this.attributes = attributes || {};
2418     this.leaf = this.attributes.leaf;
2419     /**
2420      * The node id. @type String
2421      */
2422     this.id = this.attributes.id;
2423     if(!this.id){
2424         this.id = Roo.id(null, "ynode-");
2425         this.attributes.id = this.id;
2426     }
2427      
2428     
2429     /**
2430      * All child nodes of this node. @type Array
2431      */
2432     this.childNodes = [];
2433     if(!this.childNodes.indexOf){ // indexOf is a must
2434         this.childNodes.indexOf = function(o){
2435             for(var i = 0, len = this.length; i < len; i++){
2436                 if(this[i] == o) {
2437                     return i;
2438                 }
2439             }
2440             return -1;
2441         };
2442     }
2443     /**
2444      * The parent node for this node. @type Node
2445      */
2446     this.parentNode = null;
2447     /**
2448      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2449      */
2450     this.firstChild = null;
2451     /**
2452      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2453      */
2454     this.lastChild = null;
2455     /**
2456      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2457      */
2458     this.previousSibling = null;
2459     /**
2460      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2461      */
2462     this.nextSibling = null;
2463
2464     this.addEvents({
2465        /**
2466         * @event append
2467         * Fires when a new child node is appended
2468         * @param {Tree} tree The owner tree
2469         * @param {Node} this This node
2470         * @param {Node} node The newly appended node
2471         * @param {Number} index The index of the newly appended node
2472         */
2473        "append" : true,
2474        /**
2475         * @event remove
2476         * Fires when a child node is removed
2477         * @param {Tree} tree The owner tree
2478         * @param {Node} this This node
2479         * @param {Node} node The removed node
2480         */
2481        "remove" : true,
2482        /**
2483         * @event move
2484         * Fires when this node is moved to a new location in the tree
2485         * @param {Tree} tree The owner tree
2486         * @param {Node} this This node
2487         * @param {Node} oldParent The old parent of this node
2488         * @param {Node} newParent The new parent of this node
2489         * @param {Number} index The index it was moved to
2490         */
2491        "move" : true,
2492        /**
2493         * @event insert
2494         * Fires when a new child node is inserted.
2495         * @param {Tree} tree The owner tree
2496         * @param {Node} this This node
2497         * @param {Node} node The child node inserted
2498         * @param {Node} refNode The child node the node was inserted before
2499         */
2500        "insert" : true,
2501        /**
2502         * @event beforeappend
2503         * Fires before a new child is appended, return false to cancel the append.
2504         * @param {Tree} tree The owner tree
2505         * @param {Node} this This node
2506         * @param {Node} node The child node to be appended
2507         */
2508        "beforeappend" : true,
2509        /**
2510         * @event beforeremove
2511         * Fires before a child is removed, return false to cancel the remove.
2512         * @param {Tree} tree The owner tree
2513         * @param {Node} this This node
2514         * @param {Node} node The child node to be removed
2515         */
2516        "beforeremove" : true,
2517        /**
2518         * @event beforemove
2519         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2520         * @param {Tree} tree The owner tree
2521         * @param {Node} this This node
2522         * @param {Node} oldParent The parent of this node
2523         * @param {Node} newParent The new parent this node is moving to
2524         * @param {Number} index The index it is being moved to
2525         */
2526        "beforemove" : true,
2527        /**
2528         * @event beforeinsert
2529         * Fires before a new child is inserted, return false to cancel the insert.
2530         * @param {Tree} tree The owner tree
2531         * @param {Node} this This node
2532         * @param {Node} node The child node to be inserted
2533         * @param {Node} refNode The child node the node is being inserted before
2534         */
2535        "beforeinsert" : true
2536    });
2537     this.listeners = this.attributes.listeners;
2538     Roo.data.Node.superclass.constructor.call(this);
2539 };
2540
2541 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2542     fireEvent : function(evtName){
2543         // first do standard event for this node
2544         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2545             return false;
2546         }
2547         // then bubble it up to the tree if the event wasn't cancelled
2548         var ot = this.getOwnerTree();
2549         if(ot){
2550             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2551                 return false;
2552             }
2553         }
2554         return true;
2555     },
2556
2557     /**
2558      * Returns true if this node is a leaf
2559      * @return {Boolean}
2560      */
2561     isLeaf : function(){
2562         return this.leaf === true;
2563     },
2564
2565     // private
2566     setFirstChild : function(node){
2567         this.firstChild = node;
2568     },
2569
2570     //private
2571     setLastChild : function(node){
2572         this.lastChild = node;
2573     },
2574
2575
2576     /**
2577      * Returns true if this node is the last child of its parent
2578      * @return {Boolean}
2579      */
2580     isLast : function(){
2581        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2582     },
2583
2584     /**
2585      * Returns true if this node is the first child of its parent
2586      * @return {Boolean}
2587      */
2588     isFirst : function(){
2589        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2590     },
2591
2592     hasChildNodes : function(){
2593         return !this.isLeaf() && this.childNodes.length > 0;
2594     },
2595
2596     /**
2597      * Insert node(s) as the last child node of this node.
2598      * @param {Node/Array} node The node or Array of nodes to append
2599      * @return {Node} The appended node if single append, or null if an array was passed
2600      */
2601     appendChild : function(node){
2602         var multi = false;
2603         if(node instanceof Array){
2604             multi = node;
2605         }else if(arguments.length > 1){
2606             multi = arguments;
2607         }
2608         
2609         // if passed an array or multiple args do them one by one
2610         if(multi){
2611             for(var i = 0, len = multi.length; i < len; i++) {
2612                 this.appendChild(multi[i]);
2613             }
2614         }else{
2615             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2616                 return false;
2617             }
2618             var index = this.childNodes.length;
2619             var oldParent = node.parentNode;
2620             // it's a move, make sure we move it cleanly
2621             if(oldParent){
2622                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2623                     return false;
2624                 }
2625                 oldParent.removeChild(node);
2626             }
2627             
2628             index = this.childNodes.length;
2629             if(index == 0){
2630                 this.setFirstChild(node);
2631             }
2632             this.childNodes.push(node);
2633             node.parentNode = this;
2634             var ps = this.childNodes[index-1];
2635             if(ps){
2636                 node.previousSibling = ps;
2637                 ps.nextSibling = node;
2638             }else{
2639                 node.previousSibling = null;
2640             }
2641             node.nextSibling = null;
2642             this.setLastChild(node);
2643             node.setOwnerTree(this.getOwnerTree());
2644             this.fireEvent("append", this.ownerTree, this, node, index);
2645             if(this.ownerTree) {
2646                 this.ownerTree.fireEvent("appendnode", this, node, index);
2647             }
2648             if(oldParent){
2649                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2650             }
2651             return node;
2652         }
2653     },
2654
2655     /**
2656      * Removes a child node from this node.
2657      * @param {Node} node The node to remove
2658      * @return {Node} The removed node
2659      */
2660     removeChild : function(node){
2661         var index = this.childNodes.indexOf(node);
2662         if(index == -1){
2663             return false;
2664         }
2665         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2666             return false;
2667         }
2668
2669         // remove it from childNodes collection
2670         this.childNodes.splice(index, 1);
2671
2672         // update siblings
2673         if(node.previousSibling){
2674             node.previousSibling.nextSibling = node.nextSibling;
2675         }
2676         if(node.nextSibling){
2677             node.nextSibling.previousSibling = node.previousSibling;
2678         }
2679
2680         // update child refs
2681         if(this.firstChild == node){
2682             this.setFirstChild(node.nextSibling);
2683         }
2684         if(this.lastChild == node){
2685             this.setLastChild(node.previousSibling);
2686         }
2687
2688         node.setOwnerTree(null);
2689         // clear any references from the node
2690         node.parentNode = null;
2691         node.previousSibling = null;
2692         node.nextSibling = null;
2693         this.fireEvent("remove", this.ownerTree, this, node);
2694         return node;
2695     },
2696
2697     /**
2698      * Inserts the first node before the second node in this nodes childNodes collection.
2699      * @param {Node} node The node to insert
2700      * @param {Node} refNode The node to insert before (if null the node is appended)
2701      * @return {Node} The inserted node
2702      */
2703     insertBefore : function(node, refNode){
2704         if(!refNode){ // like standard Dom, refNode can be null for append
2705             return this.appendChild(node);
2706         }
2707         // nothing to do
2708         if(node == refNode){
2709             return false;
2710         }
2711
2712         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2713             return false;
2714         }
2715         var index = this.childNodes.indexOf(refNode);
2716         var oldParent = node.parentNode;
2717         var refIndex = index;
2718
2719         // when moving internally, indexes will change after remove
2720         if(oldParent == this && this.childNodes.indexOf(node) < index){
2721             refIndex--;
2722         }
2723
2724         // it's a move, make sure we move it cleanly
2725         if(oldParent){
2726             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2727                 return false;
2728             }
2729             oldParent.removeChild(node);
2730         }
2731         if(refIndex == 0){
2732             this.setFirstChild(node);
2733         }
2734         this.childNodes.splice(refIndex, 0, node);
2735         node.parentNode = this;
2736         var ps = this.childNodes[refIndex-1];
2737         if(ps){
2738             node.previousSibling = ps;
2739             ps.nextSibling = node;
2740         }else{
2741             node.previousSibling = null;
2742         }
2743         node.nextSibling = refNode;
2744         refNode.previousSibling = node;
2745         node.setOwnerTree(this.getOwnerTree());
2746         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2747         if(oldParent){
2748             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2749         }
2750         return node;
2751     },
2752
2753     /**
2754      * Returns the child node at the specified index.
2755      * @param {Number} index
2756      * @return {Node}
2757      */
2758     item : function(index){
2759         return this.childNodes[index];
2760     },
2761
2762     /**
2763      * Replaces one child node in this node with another.
2764      * @param {Node} newChild The replacement node
2765      * @param {Node} oldChild The node to replace
2766      * @return {Node} The replaced node
2767      */
2768     replaceChild : function(newChild, oldChild){
2769         this.insertBefore(newChild, oldChild);
2770         this.removeChild(oldChild);
2771         return oldChild;
2772     },
2773
2774     /**
2775      * Returns the index of a child node
2776      * @param {Node} node
2777      * @return {Number} The index of the node or -1 if it was not found
2778      */
2779     indexOf : function(child){
2780         return this.childNodes.indexOf(child);
2781     },
2782
2783     /**
2784      * Returns the tree this node is in.
2785      * @return {Tree}
2786      */
2787     getOwnerTree : function(){
2788         // if it doesn't have one, look for one
2789         if(!this.ownerTree){
2790             var p = this;
2791             while(p){
2792                 if(p.ownerTree){
2793                     this.ownerTree = p.ownerTree;
2794                     break;
2795                 }
2796                 p = p.parentNode;
2797             }
2798         }
2799         return this.ownerTree;
2800     },
2801
2802     /**
2803      * Returns depth of this node (the root node has a depth of 0)
2804      * @return {Number}
2805      */
2806     getDepth : function(){
2807         var depth = 0;
2808         var p = this;
2809         while(p.parentNode){
2810             ++depth;
2811             p = p.parentNode;
2812         }
2813         return depth;
2814     },
2815
2816     // private
2817     setOwnerTree : function(tree){
2818         // if it's move, we need to update everyone
2819         if(tree != this.ownerTree){
2820             if(this.ownerTree){
2821                 this.ownerTree.unregisterNode(this);
2822             }
2823             this.ownerTree = tree;
2824             var cs = this.childNodes;
2825             for(var i = 0, len = cs.length; i < len; i++) {
2826                 cs[i].setOwnerTree(tree);
2827             }
2828             if(tree){
2829                 tree.registerNode(this);
2830             }
2831         }
2832     },
2833
2834     /**
2835      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2836      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2837      * @return {String} The path
2838      */
2839     getPath : function(attr){
2840         attr = attr || "id";
2841         var p = this.parentNode;
2842         var b = [this.attributes[attr]];
2843         while(p){
2844             b.unshift(p.attributes[attr]);
2845             p = p.parentNode;
2846         }
2847         var sep = this.getOwnerTree().pathSeparator;
2848         return sep + b.join(sep);
2849     },
2850
2851     /**
2852      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2853      * function call will be the scope provided or the current node. The arguments to the function
2854      * will be the args provided or the current node. If the function returns false at any point,
2855      * the bubble is stopped.
2856      * @param {Function} fn The function to call
2857      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2858      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2859      */
2860     bubble : function(fn, scope, args){
2861         var p = this;
2862         while(p){
2863             if(fn.call(scope || p, args || p) === false){
2864                 break;
2865             }
2866             p = p.parentNode;
2867         }
2868     },
2869
2870     /**
2871      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2872      * function call will be the scope provided or the current node. The arguments to the function
2873      * will be the args provided or the current node. If the function returns false at any point,
2874      * the cascade is stopped on that branch.
2875      * @param {Function} fn The function to call
2876      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2877      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2878      */
2879     cascade : function(fn, scope, args){
2880         if(fn.call(scope || this, args || this) !== false){
2881             var cs = this.childNodes;
2882             for(var i = 0, len = cs.length; i < len; i++) {
2883                 cs[i].cascade(fn, scope, args);
2884             }
2885         }
2886     },
2887
2888     /**
2889      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2890      * function call will be the scope provided or the current node. The arguments to the function
2891      * will be the args provided or the current node. If the function returns false at any point,
2892      * the iteration stops.
2893      * @param {Function} fn The function to call
2894      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2895      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2896      */
2897     eachChild : function(fn, scope, args){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(fn.call(scope || this, args || cs[i]) === false){
2901                     break;
2902                 }
2903         }
2904     },
2905
2906     /**
2907      * Finds the first child that has the attribute with the specified value.
2908      * @param {String} attribute The attribute name
2909      * @param {Mixed} value The value to search for
2910      * @return {Node} The found child or null if none was found
2911      */
2912     findChild : function(attribute, value){
2913         var cs = this.childNodes;
2914         for(var i = 0, len = cs.length; i < len; i++) {
2915                 if(cs[i].attributes[attribute] == value){
2916                     return cs[i];
2917                 }
2918         }
2919         return null;
2920     },
2921
2922     /**
2923      * Finds the first child by a custom function. The child matches if the function passed
2924      * returns true.
2925      * @param {Function} fn
2926      * @param {Object} scope (optional)
2927      * @return {Node} The found child or null if none was found
2928      */
2929     findChildBy : function(fn, scope){
2930         var cs = this.childNodes;
2931         for(var i = 0, len = cs.length; i < len; i++) {
2932                 if(fn.call(scope||cs[i], cs[i]) === true){
2933                     return cs[i];
2934                 }
2935         }
2936         return null;
2937     },
2938
2939     /**
2940      * Sorts this nodes children using the supplied sort function
2941      * @param {Function} fn
2942      * @param {Object} scope (optional)
2943      */
2944     sort : function(fn, scope){
2945         var cs = this.childNodes;
2946         var len = cs.length;
2947         if(len > 0){
2948             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2949             cs.sort(sortFn);
2950             for(var i = 0; i < len; i++){
2951                 var n = cs[i];
2952                 n.previousSibling = cs[i-1];
2953                 n.nextSibling = cs[i+1];
2954                 if(i == 0){
2955                     this.setFirstChild(n);
2956                 }
2957                 if(i == len-1){
2958                     this.setLastChild(n);
2959                 }
2960             }
2961         }
2962     },
2963
2964     /**
2965      * Returns true if this node is an ancestor (at any point) of the passed node.
2966      * @param {Node} node
2967      * @return {Boolean}
2968      */
2969     contains : function(node){
2970         return node.isAncestor(this);
2971     },
2972
2973     /**
2974      * Returns true if the passed node is an ancestor (at any point) of this node.
2975      * @param {Node} node
2976      * @return {Boolean}
2977      */
2978     isAncestor : function(node){
2979         var p = this.parentNode;
2980         while(p){
2981             if(p == node){
2982                 return true;
2983             }
2984             p = p.parentNode;
2985         }
2986         return false;
2987     },
2988
2989     toString : function(){
2990         return "[Node"+(this.id?" "+this.id:"")+"]";
2991     }
2992 });/*
2993  * Based on:
2994  * Ext JS Library 1.1.1
2995  * Copyright(c) 2006-2007, Ext JS, LLC.
2996  *
2997  * Originally Released Under LGPL - original licence link has changed is not relivant.
2998  *
2999  * Fork - LGPL
3000  * <script type="text/javascript">
3001  */
3002
3003
3004 /**
3005  * @class Roo.Shadow
3006  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3007  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3008  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3009  * @constructor
3010  * Create a new Shadow
3011  * @param {Object} config The config object
3012  */
3013 Roo.Shadow = function(config){
3014     Roo.apply(this, config);
3015     if(typeof this.mode != "string"){
3016         this.mode = this.defaultMode;
3017     }
3018     var o = this.offset, a = {h: 0};
3019     var rad = Math.floor(this.offset/2);
3020     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3021         case "drop":
3022             a.w = 0;
3023             a.l = a.t = o;
3024             a.t -= 1;
3025             if(Roo.isIE){
3026                 a.l -= this.offset + rad;
3027                 a.t -= this.offset + rad;
3028                 a.w -= rad;
3029                 a.h -= rad;
3030                 a.t += 1;
3031             }
3032         break;
3033         case "sides":
3034             a.w = (o*2);
3035             a.l = -o;
3036             a.t = o-1;
3037             if(Roo.isIE){
3038                 a.l -= (this.offset - rad);
3039                 a.t -= this.offset + rad;
3040                 a.l += 1;
3041                 a.w -= (this.offset - rad)*2;
3042                 a.w -= rad + 1;
3043                 a.h -= 1;
3044             }
3045         break;
3046         case "frame":
3047             a.w = a.h = (o*2);
3048             a.l = a.t = -o;
3049             a.t += 1;
3050             a.h -= 2;
3051             if(Roo.isIE){
3052                 a.l -= (this.offset - rad);
3053                 a.t -= (this.offset - rad);
3054                 a.l += 1;
3055                 a.w -= (this.offset + rad + 1);
3056                 a.h -= (this.offset + rad);
3057                 a.h += 1;
3058             }
3059         break;
3060     };
3061
3062     this.adjusts = a;
3063 };
3064
3065 Roo.Shadow.prototype = {
3066     /**
3067      * @cfg {String} mode
3068      * The shadow display mode.  Supports the following options:<br />
3069      * sides: Shadow displays on both sides and bottom only<br />
3070      * frame: Shadow displays equally on all four sides<br />
3071      * drop: Traditional bottom-right drop shadow (default)
3072      */
3073     mode: false,
3074     /**
3075      * @cfg {String} offset
3076      * The number of pixels to offset the shadow from the element (defaults to 4)
3077      */
3078     offset: 4,
3079
3080     // private
3081     defaultMode: "drop",
3082
3083     /**
3084      * Displays the shadow under the target element
3085      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3086      */
3087     show : function(target){
3088         target = Roo.get(target);
3089         if(!this.el){
3090             this.el = Roo.Shadow.Pool.pull();
3091             if(this.el.dom.nextSibling != target.dom){
3092                 this.el.insertBefore(target);
3093             }
3094         }
3095         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3096         if(Roo.isIE){
3097             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3098         }
3099         this.realign(
3100             target.getLeft(true),
3101             target.getTop(true),
3102             target.getWidth(),
3103             target.getHeight()
3104         );
3105         this.el.dom.style.display = "block";
3106     },
3107
3108     /**
3109      * Returns true if the shadow is visible, else false
3110      */
3111     isVisible : function(){
3112         return this.el ? true : false;  
3113     },
3114
3115     /**
3116      * Direct alignment when values are already available. Show must be called at least once before
3117      * calling this method to ensure it is initialized.
3118      * @param {Number} left The target element left position
3119      * @param {Number} top The target element top position
3120      * @param {Number} width The target element width
3121      * @param {Number} height The target element height
3122      */
3123     realign : function(l, t, w, h){
3124         if(!this.el){
3125             return;
3126         }
3127         var a = this.adjusts, d = this.el.dom, s = d.style;
3128         var iea = 0;
3129         s.left = (l+a.l)+"px";
3130         s.top = (t+a.t)+"px";
3131         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3132  
3133         if(s.width != sws || s.height != shs){
3134             s.width = sws;
3135             s.height = shs;
3136             if(!Roo.isIE){
3137                 var cn = d.childNodes;
3138                 var sww = Math.max(0, (sw-12))+"px";
3139                 cn[0].childNodes[1].style.width = sww;
3140                 cn[1].childNodes[1].style.width = sww;
3141                 cn[2].childNodes[1].style.width = sww;
3142                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3143             }
3144         }
3145     },
3146
3147     /**
3148      * Hides this shadow
3149      */
3150     hide : function(){
3151         if(this.el){
3152             this.el.dom.style.display = "none";
3153             Roo.Shadow.Pool.push(this.el);
3154             delete this.el;
3155         }
3156     },
3157
3158     /**
3159      * Adjust the z-index of this shadow
3160      * @param {Number} zindex The new z-index
3161      */
3162     setZIndex : function(z){
3163         this.zIndex = z;
3164         if(this.el){
3165             this.el.setStyle("z-index", z);
3166         }
3167     }
3168 };
3169
3170 // Private utility class that manages the internal Shadow cache
3171 Roo.Shadow.Pool = function(){
3172     var p = [];
3173     var markup = Roo.isIE ?
3174                  '<div class="x-ie-shadow"></div>' :
3175                  '<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>';
3176     return {
3177         pull : function(){
3178             var sh = p.shift();
3179             if(!sh){
3180                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3181                 sh.autoBoxAdjust = false;
3182             }
3183             return sh;
3184         },
3185
3186         push : function(sh){
3187             p.push(sh);
3188         }
3189     };
3190 }();/*
3191  * Based on:
3192  * Ext JS Library 1.1.1
3193  * Copyright(c) 2006-2007, Ext JS, LLC.
3194  *
3195  * Originally Released Under LGPL - original licence link has changed is not relivant.
3196  *
3197  * Fork - LGPL
3198  * <script type="text/javascript">
3199  */
3200
3201
3202 /**
3203  * @class Roo.SplitBar
3204  * @extends Roo.util.Observable
3205  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3206  * <br><br>
3207  * Usage:
3208  * <pre><code>
3209 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3210                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3211 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3212 split.minSize = 100;
3213 split.maxSize = 600;
3214 split.animate = true;
3215 split.on('moved', splitterMoved);
3216 </code></pre>
3217  * @constructor
3218  * Create a new SplitBar
3219  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3220  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3221  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3222  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3223                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3224                         position of the SplitBar).
3225  */
3226 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3227     
3228     /** @private */
3229     this.el = Roo.get(dragElement, true);
3230     this.el.dom.unselectable = "on";
3231     /** @private */
3232     this.resizingEl = Roo.get(resizingElement, true);
3233
3234     /**
3235      * @private
3236      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3237      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3238      * @type Number
3239      */
3240     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3241     
3242     /**
3243      * The minimum size of the resizing element. (Defaults to 0)
3244      * @type Number
3245      */
3246     this.minSize = 0;
3247     
3248     /**
3249      * The maximum size of the resizing element. (Defaults to 2000)
3250      * @type Number
3251      */
3252     this.maxSize = 2000;
3253     
3254     /**
3255      * Whether to animate the transition to the new size
3256      * @type Boolean
3257      */
3258     this.animate = false;
3259     
3260     /**
3261      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3262      * @type Boolean
3263      */
3264     this.useShim = false;
3265     
3266     /** @private */
3267     this.shim = null;
3268     
3269     if(!existingProxy){
3270         /** @private */
3271         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3272     }else{
3273         this.proxy = Roo.get(existingProxy).dom;
3274     }
3275     /** @private */
3276     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3277     
3278     /** @private */
3279     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3280     
3281     /** @private */
3282     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3283     
3284     /** @private */
3285     this.dragSpecs = {};
3286     
3287     /**
3288      * @private The adapter to use to positon and resize elements
3289      */
3290     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3291     this.adapter.init(this);
3292     
3293     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3294         /** @private */
3295         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3296         this.el.addClass("x-splitbar-h");
3297     }else{
3298         /** @private */
3299         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3300         this.el.addClass("x-splitbar-v");
3301     }
3302     
3303     this.addEvents({
3304         /**
3305          * @event resize
3306          * Fires when the splitter is moved (alias for {@link #event-moved})
3307          * @param {Roo.SplitBar} this
3308          * @param {Number} newSize the new width or height
3309          */
3310         "resize" : true,
3311         /**
3312          * @event moved
3313          * Fires when the splitter is moved
3314          * @param {Roo.SplitBar} this
3315          * @param {Number} newSize the new width or height
3316          */
3317         "moved" : true,
3318         /**
3319          * @event beforeresize
3320          * Fires before the splitter is dragged
3321          * @param {Roo.SplitBar} this
3322          */
3323         "beforeresize" : true,
3324
3325         "beforeapply" : true
3326     });
3327
3328     Roo.util.Observable.call(this);
3329 };
3330
3331 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3332     onStartProxyDrag : function(x, y){
3333         this.fireEvent("beforeresize", this);
3334         if(!this.overlay){
3335             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3336             o.unselectable();
3337             o.enableDisplayMode("block");
3338             // all splitbars share the same overlay
3339             Roo.SplitBar.prototype.overlay = o;
3340         }
3341         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3342         this.overlay.show();
3343         Roo.get(this.proxy).setDisplayed("block");
3344         var size = this.adapter.getElementSize(this);
3345         this.activeMinSize = this.getMinimumSize();;
3346         this.activeMaxSize = this.getMaximumSize();;
3347         var c1 = size - this.activeMinSize;
3348         var c2 = Math.max(this.activeMaxSize - size, 0);
3349         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3350             this.dd.resetConstraints();
3351             this.dd.setXConstraint(
3352                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3353                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3354             );
3355             this.dd.setYConstraint(0, 0);
3356         }else{
3357             this.dd.resetConstraints();
3358             this.dd.setXConstraint(0, 0);
3359             this.dd.setYConstraint(
3360                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3361                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3362             );
3363          }
3364         this.dragSpecs.startSize = size;
3365         this.dragSpecs.startPoint = [x, y];
3366         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3367     },
3368     
3369     /** 
3370      * @private Called after the drag operation by the DDProxy
3371      */
3372     onEndProxyDrag : function(e){
3373         Roo.get(this.proxy).setDisplayed(false);
3374         var endPoint = Roo.lib.Event.getXY(e);
3375         if(this.overlay){
3376             this.overlay.hide();
3377         }
3378         var newSize;
3379         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3380             newSize = this.dragSpecs.startSize + 
3381                 (this.placement == Roo.SplitBar.LEFT ?
3382                     endPoint[0] - this.dragSpecs.startPoint[0] :
3383                     this.dragSpecs.startPoint[0] - endPoint[0]
3384                 );
3385         }else{
3386             newSize = this.dragSpecs.startSize + 
3387                 (this.placement == Roo.SplitBar.TOP ?
3388                     endPoint[1] - this.dragSpecs.startPoint[1] :
3389                     this.dragSpecs.startPoint[1] - endPoint[1]
3390                 );
3391         }
3392         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3393         if(newSize != this.dragSpecs.startSize){
3394             if(this.fireEvent('beforeapply', this, newSize) !== false){
3395                 this.adapter.setElementSize(this, newSize);
3396                 this.fireEvent("moved", this, newSize);
3397                 this.fireEvent("resize", this, newSize);
3398             }
3399         }
3400     },
3401     
3402     /**
3403      * Get the adapter this SplitBar uses
3404      * @return The adapter object
3405      */
3406     getAdapter : function(){
3407         return this.adapter;
3408     },
3409     
3410     /**
3411      * Set the adapter this SplitBar uses
3412      * @param {Object} adapter A SplitBar adapter object
3413      */
3414     setAdapter : function(adapter){
3415         this.adapter = adapter;
3416         this.adapter.init(this);
3417     },
3418     
3419     /**
3420      * Gets the minimum size for the resizing element
3421      * @return {Number} The minimum size
3422      */
3423     getMinimumSize : function(){
3424         return this.minSize;
3425     },
3426     
3427     /**
3428      * Sets the minimum size for the resizing element
3429      * @param {Number} minSize The minimum size
3430      */
3431     setMinimumSize : function(minSize){
3432         this.minSize = minSize;
3433     },
3434     
3435     /**
3436      * Gets the maximum size for the resizing element
3437      * @return {Number} The maximum size
3438      */
3439     getMaximumSize : function(){
3440         return this.maxSize;
3441     },
3442     
3443     /**
3444      * Sets the maximum size for the resizing element
3445      * @param {Number} maxSize The maximum size
3446      */
3447     setMaximumSize : function(maxSize){
3448         this.maxSize = maxSize;
3449     },
3450     
3451     /**
3452      * Sets the initialize size for the resizing element
3453      * @param {Number} size The initial size
3454      */
3455     setCurrentSize : function(size){
3456         var oldAnimate = this.animate;
3457         this.animate = false;
3458         this.adapter.setElementSize(this, size);
3459         this.animate = oldAnimate;
3460     },
3461     
3462     /**
3463      * Destroy this splitbar. 
3464      * @param {Boolean} removeEl True to remove the element
3465      */
3466     destroy : function(removeEl){
3467         if(this.shim){
3468             this.shim.remove();
3469         }
3470         this.dd.unreg();
3471         this.proxy.parentNode.removeChild(this.proxy);
3472         if(removeEl){
3473             this.el.remove();
3474         }
3475     }
3476 });
3477
3478 /**
3479  * @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.
3480  */
3481 Roo.SplitBar.createProxy = function(dir){
3482     var proxy = new Roo.Element(document.createElement("div"));
3483     proxy.unselectable();
3484     var cls = 'x-splitbar-proxy';
3485     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3486     document.body.appendChild(proxy.dom);
3487     return proxy.dom;
3488 };
3489
3490 /** 
3491  * @class Roo.SplitBar.BasicLayoutAdapter
3492  * Default Adapter. It assumes the splitter and resizing element are not positioned
3493  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3494  */
3495 Roo.SplitBar.BasicLayoutAdapter = function(){
3496 };
3497
3498 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3499     // do nothing for now
3500     init : function(s){
3501     
3502     },
3503     /**
3504      * Called before drag operations to get the current size of the resizing element. 
3505      * @param {Roo.SplitBar} s The SplitBar using this adapter
3506      */
3507      getElementSize : function(s){
3508         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3509             return s.resizingEl.getWidth();
3510         }else{
3511             return s.resizingEl.getHeight();
3512         }
3513     },
3514     
3515     /**
3516      * Called after drag operations to set the size of the resizing element.
3517      * @param {Roo.SplitBar} s The SplitBar using this adapter
3518      * @param {Number} newSize The new size to set
3519      * @param {Function} onComplete A function to be invoked when resizing is complete
3520      */
3521     setElementSize : function(s, newSize, onComplete){
3522         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3523             if(!s.animate){
3524                 s.resizingEl.setWidth(newSize);
3525                 if(onComplete){
3526                     onComplete(s, newSize);
3527                 }
3528             }else{
3529                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3530             }
3531         }else{
3532             
3533             if(!s.animate){
3534                 s.resizingEl.setHeight(newSize);
3535                 if(onComplete){
3536                     onComplete(s, newSize);
3537                 }
3538             }else{
3539                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3540             }
3541         }
3542     }
3543 };
3544
3545 /** 
3546  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3547  * @extends Roo.SplitBar.BasicLayoutAdapter
3548  * Adapter that  moves the splitter element to align with the resized sizing element. 
3549  * Used with an absolute positioned SplitBar.
3550  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3551  * document.body, make sure you assign an id to the body element.
3552  */
3553 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3554     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3555     this.container = Roo.get(container);
3556 };
3557
3558 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3559     init : function(s){
3560         this.basic.init(s);
3561     },
3562     
3563     getElementSize : function(s){
3564         return this.basic.getElementSize(s);
3565     },
3566     
3567     setElementSize : function(s, newSize, onComplete){
3568         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3569     },
3570     
3571     moveSplitter : function(s){
3572         var yes = Roo.SplitBar;
3573         switch(s.placement){
3574             case yes.LEFT:
3575                 s.el.setX(s.resizingEl.getRight());
3576                 break;
3577             case yes.RIGHT:
3578                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3579                 break;
3580             case yes.TOP:
3581                 s.el.setY(s.resizingEl.getBottom());
3582                 break;
3583             case yes.BOTTOM:
3584                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3585                 break;
3586         }
3587     }
3588 };
3589
3590 /**
3591  * Orientation constant - Create a vertical SplitBar
3592  * @static
3593  * @type Number
3594  */
3595 Roo.SplitBar.VERTICAL = 1;
3596
3597 /**
3598  * Orientation constant - Create a horizontal SplitBar
3599  * @static
3600  * @type Number
3601  */
3602 Roo.SplitBar.HORIZONTAL = 2;
3603
3604 /**
3605  * Placement constant - The resizing element is to the left of the splitter element
3606  * @static
3607  * @type Number
3608  */
3609 Roo.SplitBar.LEFT = 1;
3610
3611 /**
3612  * Placement constant - The resizing element is to the right of the splitter element
3613  * @static
3614  * @type Number
3615  */
3616 Roo.SplitBar.RIGHT = 2;
3617
3618 /**
3619  * Placement constant - The resizing element is positioned above the splitter element
3620  * @static
3621  * @type Number
3622  */
3623 Roo.SplitBar.TOP = 3;
3624
3625 /**
3626  * Placement constant - The resizing element is positioned under splitter element
3627  * @static
3628  * @type Number
3629  */
3630 Roo.SplitBar.BOTTOM = 4;
3631 /*
3632  * Based on:
3633  * Ext JS Library 1.1.1
3634  * Copyright(c) 2006-2007, Ext JS, LLC.
3635  *
3636  * Originally Released Under LGPL - original licence link has changed is not relivant.
3637  *
3638  * Fork - LGPL
3639  * <script type="text/javascript">
3640  */
3641
3642 /**
3643  * @class Roo.View
3644  * @extends Roo.util.Observable
3645  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3646  * This class also supports single and multi selection modes. <br>
3647  * Create a data model bound view:
3648  <pre><code>
3649  var store = new Roo.data.Store(...);
3650
3651  var view = new Roo.View({
3652     el : "my-element",
3653     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3654  
3655     singleSelect: true,
3656     selectedClass: "ydataview-selected",
3657     store: store
3658  });
3659
3660  // listen for node click?
3661  view.on("click", function(vw, index, node, e){
3662  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3663  });
3664
3665  // load XML data
3666  dataModel.load("foobar.xml");
3667  </code></pre>
3668  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3669  * <br><br>
3670  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3671  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3672  * 
3673  * Note: old style constructor is still suported (container, template, config)
3674  * 
3675  * @constructor
3676  * Create a new View
3677  * @param {Object} config The config object
3678  * 
3679  */
3680 Roo.View = function(config, depreciated_tpl, depreciated_config){
3681     
3682     this.parent = false;
3683     
3684     if (typeof(depreciated_tpl) == 'undefined') {
3685         // new way.. - universal constructor.
3686         Roo.apply(this, config);
3687         this.el  = Roo.get(this.el);
3688     } else {
3689         // old format..
3690         this.el  = Roo.get(config);
3691         this.tpl = depreciated_tpl;
3692         Roo.apply(this, depreciated_config);
3693     }
3694     this.wrapEl  = this.el.wrap().wrap();
3695     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3696     
3697     
3698     if(typeof(this.tpl) == "string"){
3699         this.tpl = new Roo.Template(this.tpl);
3700     } else {
3701         // support xtype ctors..
3702         this.tpl = new Roo.factory(this.tpl, Roo);
3703     }
3704     
3705     
3706     this.tpl.compile();
3707     
3708     /** @private */
3709     this.addEvents({
3710         /**
3711          * @event beforeclick
3712          * Fires before a click is processed. Returns false to cancel the default action.
3713          * @param {Roo.View} this
3714          * @param {Number} index The index of the target node
3715          * @param {HTMLElement} node The target node
3716          * @param {Roo.EventObject} e The raw event object
3717          */
3718             "beforeclick" : true,
3719         /**
3720          * @event click
3721          * Fires when a template node is clicked.
3722          * @param {Roo.View} this
3723          * @param {Number} index The index of the target node
3724          * @param {HTMLElement} node The target node
3725          * @param {Roo.EventObject} e The raw event object
3726          */
3727             "click" : true,
3728         /**
3729          * @event dblclick
3730          * Fires when a template node is double clicked.
3731          * @param {Roo.View} this
3732          * @param {Number} index The index of the target node
3733          * @param {HTMLElement} node The target node
3734          * @param {Roo.EventObject} e The raw event object
3735          */
3736             "dblclick" : true,
3737         /**
3738          * @event contextmenu
3739          * Fires when a template node is right clicked.
3740          * @param {Roo.View} this
3741          * @param {Number} index The index of the target node
3742          * @param {HTMLElement} node The target node
3743          * @param {Roo.EventObject} e The raw event object
3744          */
3745             "contextmenu" : true,
3746         /**
3747          * @event selectionchange
3748          * Fires when the selected nodes change.
3749          * @param {Roo.View} this
3750          * @param {Array} selections Array of the selected nodes
3751          */
3752             "selectionchange" : true,
3753     
3754         /**
3755          * @event beforeselect
3756          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3757          * @param {Roo.View} this
3758          * @param {HTMLElement} node The node to be selected
3759          * @param {Array} selections Array of currently selected nodes
3760          */
3761             "beforeselect" : true,
3762         /**
3763          * @event preparedata
3764          * Fires on every row to render, to allow you to change the data.
3765          * @param {Roo.View} this
3766          * @param {Object} data to be rendered (change this)
3767          */
3768           "preparedata" : true
3769           
3770           
3771         });
3772
3773
3774
3775     this.el.on({
3776         "click": this.onClick,
3777         "dblclick": this.onDblClick,
3778         "contextmenu": this.onContextMenu,
3779         scope:this
3780     });
3781
3782     this.selections = [];
3783     this.nodes = [];
3784     this.cmp = new Roo.CompositeElementLite([]);
3785     if(this.store){
3786         this.store = Roo.factory(this.store, Roo.data);
3787         this.setStore(this.store, true);
3788     }
3789     
3790     if ( this.footer && this.footer.xtype) {
3791            
3792          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3793         
3794         this.footer.dataSource = this.store;
3795         this.footer.container = fctr;
3796         this.footer = Roo.factory(this.footer, Roo);
3797         fctr.insertFirst(this.el);
3798         
3799         // this is a bit insane - as the paging toolbar seems to detach the el..
3800 //        dom.parentNode.parentNode.parentNode
3801          // they get detached?
3802     }
3803     
3804     
3805     Roo.View.superclass.constructor.call(this);
3806     
3807     
3808 };
3809
3810 Roo.extend(Roo.View, Roo.util.Observable, {
3811     
3812      /**
3813      * @cfg {Roo.data.Store} store Data store to load data from.
3814      */
3815     store : false,
3816     
3817     /**
3818      * @cfg {String|Roo.Element} el The container element.
3819      */
3820     el : '',
3821     
3822     /**
3823      * @cfg {String|Roo.Template} tpl The template used by this View 
3824      */
3825     tpl : false,
3826     /**
3827      * @cfg {String} dataName the named area of the template to use as the data area
3828      *                          Works with domtemplates roo-name="name"
3829      */
3830     dataName: false,
3831     /**
3832      * @cfg {String} selectedClass The css class to add to selected nodes
3833      */
3834     selectedClass : "x-view-selected",
3835      /**
3836      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3837      */
3838     emptyText : "",
3839     
3840     /**
3841      * @cfg {String} text to display on mask (default Loading)
3842      */
3843     mask : false,
3844     /**
3845      * @cfg {Boolean} multiSelect Allow multiple selection
3846      */
3847     multiSelect : false,
3848     /**
3849      * @cfg {Boolean} singleSelect Allow single selection
3850      */
3851     singleSelect:  false,
3852     
3853     /**
3854      * @cfg {Boolean} toggleSelect - selecting 
3855      */
3856     toggleSelect : false,
3857     
3858     /**
3859      * @cfg {Boolean} tickable - selecting 
3860      */
3861     tickable : false,
3862     
3863     /**
3864      * Returns the element this view is bound to.
3865      * @return {Roo.Element}
3866      */
3867     getEl : function(){
3868         return this.wrapEl;
3869     },
3870     
3871     
3872
3873     /**
3874      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3875      */
3876     refresh : function(){
3877         //Roo.log('refresh');
3878         var t = this.tpl;
3879         
3880         // if we are using something like 'domtemplate', then
3881         // the what gets used is:
3882         // t.applySubtemplate(NAME, data, wrapping data..)
3883         // the outer template then get' applied with
3884         //     the store 'extra data'
3885         // and the body get's added to the
3886         //      roo-name="data" node?
3887         //      <span class='roo-tpl-{name}'></span> ?????
3888         
3889         
3890         
3891         this.clearSelections();
3892         this.el.update("");
3893         var html = [];
3894         var records = this.store.getRange();
3895         if(records.length < 1) {
3896             
3897             // is this valid??  = should it render a template??
3898             
3899             this.el.update(this.emptyText);
3900             return;
3901         }
3902         var el = this.el;
3903         if (this.dataName) {
3904             this.el.update(t.apply(this.store.meta)); //????
3905             el = this.el.child('.roo-tpl-' + this.dataName);
3906         }
3907         
3908         for(var i = 0, len = records.length; i < len; i++){
3909             var data = this.prepareData(records[i].data, i, records[i]);
3910             this.fireEvent("preparedata", this, data, i, records[i]);
3911             
3912             var d = Roo.apply({}, data);
3913             
3914             if(this.tickable){
3915                 Roo.apply(d, {'roo-id' : Roo.id()});
3916                 
3917                 var _this = this;
3918             
3919                 Roo.each(this.parent.item, function(item){
3920                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3921                         return;
3922                     }
3923                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3924                 });
3925             }
3926             
3927             html[html.length] = Roo.util.Format.trim(
3928                 this.dataName ?
3929                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3930                     t.apply(d)
3931             );
3932         }
3933         
3934         
3935         
3936         el.update(html.join(""));
3937         this.nodes = el.dom.childNodes;
3938         this.updateIndexes(0);
3939     },
3940     
3941
3942     /**
3943      * Function to override to reformat the data that is sent to
3944      * the template for each node.
3945      * DEPRICATED - use the preparedata event handler.
3946      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3947      * a JSON object for an UpdateManager bound view).
3948      */
3949     prepareData : function(data, index, record)
3950     {
3951         this.fireEvent("preparedata", this, data, index, record);
3952         return data;
3953     },
3954
3955     onUpdate : function(ds, record){
3956         // Roo.log('on update');   
3957         this.clearSelections();
3958         var index = this.store.indexOf(record);
3959         var n = this.nodes[index];
3960         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3961         n.parentNode.removeChild(n);
3962         this.updateIndexes(index, index);
3963     },
3964
3965     
3966     
3967 // --------- FIXME     
3968     onAdd : function(ds, records, index)
3969     {
3970         //Roo.log(['on Add', ds, records, index] );        
3971         this.clearSelections();
3972         if(this.nodes.length == 0){
3973             this.refresh();
3974             return;
3975         }
3976         var n = this.nodes[index];
3977         for(var i = 0, len = records.length; i < len; i++){
3978             var d = this.prepareData(records[i].data, i, records[i]);
3979             if(n){
3980                 this.tpl.insertBefore(n, d);
3981             }else{
3982                 
3983                 this.tpl.append(this.el, d);
3984             }
3985         }
3986         this.updateIndexes(index);
3987     },
3988
3989     onRemove : function(ds, record, index){
3990        // Roo.log('onRemove');
3991         this.clearSelections();
3992         var el = this.dataName  ?
3993             this.el.child('.roo-tpl-' + this.dataName) :
3994             this.el; 
3995         
3996         el.dom.removeChild(this.nodes[index]);
3997         this.updateIndexes(index);
3998     },
3999
4000     /**
4001      * Refresh an individual node.
4002      * @param {Number} index
4003      */
4004     refreshNode : function(index){
4005         this.onUpdate(this.store, this.store.getAt(index));
4006     },
4007
4008     updateIndexes : function(startIndex, endIndex){
4009         var ns = this.nodes;
4010         startIndex = startIndex || 0;
4011         endIndex = endIndex || ns.length - 1;
4012         for(var i = startIndex; i <= endIndex; i++){
4013             ns[i].nodeIndex = i;
4014         }
4015     },
4016
4017     /**
4018      * Changes the data store this view uses and refresh the view.
4019      * @param {Store} store
4020      */
4021     setStore : function(store, initial){
4022         if(!initial && this.store){
4023             this.store.un("datachanged", this.refresh);
4024             this.store.un("add", this.onAdd);
4025             this.store.un("remove", this.onRemove);
4026             this.store.un("update", this.onUpdate);
4027             this.store.un("clear", this.refresh);
4028             this.store.un("beforeload", this.onBeforeLoad);
4029             this.store.un("load", this.onLoad);
4030             this.store.un("loadexception", this.onLoad);
4031         }
4032         if(store){
4033           
4034             store.on("datachanged", this.refresh, this);
4035             store.on("add", this.onAdd, this);
4036             store.on("remove", this.onRemove, this);
4037             store.on("update", this.onUpdate, this);
4038             store.on("clear", this.refresh, this);
4039             store.on("beforeload", this.onBeforeLoad, this);
4040             store.on("load", this.onLoad, this);
4041             store.on("loadexception", this.onLoad, this);
4042         }
4043         
4044         if(store){
4045             this.refresh();
4046         }
4047     },
4048     /**
4049      * onbeforeLoad - masks the loading area.
4050      *
4051      */
4052     onBeforeLoad : function(store,opts)
4053     {
4054          //Roo.log('onBeforeLoad');   
4055         if (!opts.add) {
4056             this.el.update("");
4057         }
4058         this.el.mask(this.mask ? this.mask : "Loading" ); 
4059     },
4060     onLoad : function ()
4061     {
4062         this.el.unmask();
4063     },
4064     
4065
4066     /**
4067      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4068      * @param {HTMLElement} node
4069      * @return {HTMLElement} The template node
4070      */
4071     findItemFromChild : function(node){
4072         var el = this.dataName  ?
4073             this.el.child('.roo-tpl-' + this.dataName,true) :
4074             this.el.dom; 
4075         
4076         if(!node || node.parentNode == el){
4077                     return node;
4078             }
4079             var p = node.parentNode;
4080             while(p && p != el){
4081             if(p.parentNode == el){
4082                 return p;
4083             }
4084             p = p.parentNode;
4085         }
4086             return null;
4087     },
4088
4089     /** @ignore */
4090     onClick : function(e){
4091         var item = this.findItemFromChild(e.getTarget());
4092         if(item){
4093             var index = this.indexOf(item);
4094             if(this.onItemClick(item, index, e) !== false){
4095                 this.fireEvent("click", this, index, item, e);
4096             }
4097         }else{
4098             this.clearSelections();
4099         }
4100     },
4101
4102     /** @ignore */
4103     onContextMenu : function(e){
4104         var item = this.findItemFromChild(e.getTarget());
4105         if(item){
4106             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4107         }
4108     },
4109
4110     /** @ignore */
4111     onDblClick : function(e){
4112         var item = this.findItemFromChild(e.getTarget());
4113         if(item){
4114             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4115         }
4116     },
4117
4118     onItemClick : function(item, index, e)
4119     {
4120         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4121             return false;
4122         }
4123         if (this.toggleSelect) {
4124             var m = this.isSelected(item) ? 'unselect' : 'select';
4125             //Roo.log(m);
4126             var _t = this;
4127             _t[m](item, true, false);
4128             return true;
4129         }
4130         if(this.multiSelect || this.singleSelect){
4131             if(this.multiSelect && e.shiftKey && this.lastSelection){
4132                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4133             }else{
4134                 this.select(item, this.multiSelect && e.ctrlKey);
4135                 this.lastSelection = item;
4136             }
4137             
4138             if(!this.tickable){
4139                 e.preventDefault();
4140             }
4141             
4142         }
4143         return true;
4144     },
4145
4146     /**
4147      * Get the number of selected nodes.
4148      * @return {Number}
4149      */
4150     getSelectionCount : function(){
4151         return this.selections.length;
4152     },
4153
4154     /**
4155      * Get the currently selected nodes.
4156      * @return {Array} An array of HTMLElements
4157      */
4158     getSelectedNodes : function(){
4159         return this.selections;
4160     },
4161
4162     /**
4163      * Get the indexes of the selected nodes.
4164      * @return {Array}
4165      */
4166     getSelectedIndexes : function(){
4167         var indexes = [], s = this.selections;
4168         for(var i = 0, len = s.length; i < len; i++){
4169             indexes.push(s[i].nodeIndex);
4170         }
4171         return indexes;
4172     },
4173
4174     /**
4175      * Clear all selections
4176      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4177      */
4178     clearSelections : function(suppressEvent){
4179         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4180             this.cmp.elements = this.selections;
4181             this.cmp.removeClass(this.selectedClass);
4182             this.selections = [];
4183             if(!suppressEvent){
4184                 this.fireEvent("selectionchange", this, this.selections);
4185             }
4186         }
4187     },
4188
4189     /**
4190      * Returns true if the passed node is selected
4191      * @param {HTMLElement/Number} node The node or node index
4192      * @return {Boolean}
4193      */
4194     isSelected : function(node){
4195         var s = this.selections;
4196         if(s.length < 1){
4197             return false;
4198         }
4199         node = this.getNode(node);
4200         return s.indexOf(node) !== -1;
4201     },
4202
4203     /**
4204      * Selects nodes.
4205      * @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
4206      * @param {Boolean} keepExisting (optional) true to keep existing selections
4207      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4208      */
4209     select : function(nodeInfo, keepExisting, suppressEvent){
4210         if(nodeInfo instanceof Array){
4211             if(!keepExisting){
4212                 this.clearSelections(true);
4213             }
4214             for(var i = 0, len = nodeInfo.length; i < len; i++){
4215                 this.select(nodeInfo[i], true, true);
4216             }
4217             return;
4218         } 
4219         var node = this.getNode(nodeInfo);
4220         if(!node || this.isSelected(node)){
4221             return; // already selected.
4222         }
4223         if(!keepExisting){
4224             this.clearSelections(true);
4225         }
4226         
4227         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4228             Roo.fly(node).addClass(this.selectedClass);
4229             this.selections.push(node);
4230             if(!suppressEvent){
4231                 this.fireEvent("selectionchange", this, this.selections);
4232             }
4233         }
4234         
4235         
4236     },
4237       /**
4238      * Unselects nodes.
4239      * @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
4240      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4241      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4242      */
4243     unselect : function(nodeInfo, keepExisting, suppressEvent)
4244     {
4245         if(nodeInfo instanceof Array){
4246             Roo.each(this.selections, function(s) {
4247                 this.unselect(s, nodeInfo);
4248             }, this);
4249             return;
4250         }
4251         var node = this.getNode(nodeInfo);
4252         if(!node || !this.isSelected(node)){
4253             //Roo.log("not selected");
4254             return; // not selected.
4255         }
4256         // fireevent???
4257         var ns = [];
4258         Roo.each(this.selections, function(s) {
4259             if (s == node ) {
4260                 Roo.fly(node).removeClass(this.selectedClass);
4261
4262                 return;
4263             }
4264             ns.push(s);
4265         },this);
4266         
4267         this.selections= ns;
4268         this.fireEvent("selectionchange", this, this.selections);
4269     },
4270
4271     /**
4272      * Gets a template node.
4273      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4274      * @return {HTMLElement} The node or null if it wasn't found
4275      */
4276     getNode : function(nodeInfo){
4277         if(typeof nodeInfo == "string"){
4278             return document.getElementById(nodeInfo);
4279         }else if(typeof nodeInfo == "number"){
4280             return this.nodes[nodeInfo];
4281         }
4282         return nodeInfo;
4283     },
4284
4285     /**
4286      * Gets a range template nodes.
4287      * @param {Number} startIndex
4288      * @param {Number} endIndex
4289      * @return {Array} An array of nodes
4290      */
4291     getNodes : function(start, end){
4292         var ns = this.nodes;
4293         start = start || 0;
4294         end = typeof end == "undefined" ? ns.length - 1 : end;
4295         var nodes = [];
4296         if(start <= end){
4297             for(var i = start; i <= end; i++){
4298                 nodes.push(ns[i]);
4299             }
4300         } else{
4301             for(var i = start; i >= end; i--){
4302                 nodes.push(ns[i]);
4303             }
4304         }
4305         return nodes;
4306     },
4307
4308     /**
4309      * Finds the index of the passed node
4310      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4311      * @return {Number} The index of the node or -1
4312      */
4313     indexOf : function(node){
4314         node = this.getNode(node);
4315         if(typeof node.nodeIndex == "number"){
4316             return node.nodeIndex;
4317         }
4318         var ns = this.nodes;
4319         for(var i = 0, len = ns.length; i < len; i++){
4320             if(ns[i] == node){
4321                 return i;
4322             }
4323         }
4324         return -1;
4325     }
4326 });
4327 /*
4328  * Based on:
4329  * Ext JS Library 1.1.1
4330  * Copyright(c) 2006-2007, Ext JS, LLC.
4331  *
4332  * Originally Released Under LGPL - original licence link has changed is not relivant.
4333  *
4334  * Fork - LGPL
4335  * <script type="text/javascript">
4336  */
4337
4338 /**
4339  * @class Roo.JsonView
4340  * @extends Roo.View
4341  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4342 <pre><code>
4343 var view = new Roo.JsonView({
4344     container: "my-element",
4345     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4346     multiSelect: true, 
4347     jsonRoot: "data" 
4348 });
4349
4350 // listen for node click?
4351 view.on("click", function(vw, index, node, e){
4352     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4353 });
4354
4355 // direct load of JSON data
4356 view.load("foobar.php");
4357
4358 // Example from my blog list
4359 var tpl = new Roo.Template(
4360     '&lt;div class="entry"&gt;' +
4361     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4362     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4363     "&lt;/div&gt;&lt;hr /&gt;"
4364 );
4365
4366 var moreView = new Roo.JsonView({
4367     container :  "entry-list", 
4368     template : tpl,
4369     jsonRoot: "posts"
4370 });
4371 moreView.on("beforerender", this.sortEntries, this);
4372 moreView.load({
4373     url: "/blog/get-posts.php",
4374     params: "allposts=true",
4375     text: "Loading Blog Entries..."
4376 });
4377 </code></pre>
4378
4379 * Note: old code is supported with arguments : (container, template, config)
4380
4381
4382  * @constructor
4383  * Create a new JsonView
4384  * 
4385  * @param {Object} config The config object
4386  * 
4387  */
4388 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4389     
4390     
4391     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4392
4393     var um = this.el.getUpdateManager();
4394     um.setRenderer(this);
4395     um.on("update", this.onLoad, this);
4396     um.on("failure", this.onLoadException, this);
4397
4398     /**
4399      * @event beforerender
4400      * Fires before rendering of the downloaded JSON data.
4401      * @param {Roo.JsonView} this
4402      * @param {Object} data The JSON data loaded
4403      */
4404     /**
4405      * @event load
4406      * Fires when data is loaded.
4407      * @param {Roo.JsonView} this
4408      * @param {Object} data The JSON data loaded
4409      * @param {Object} response The raw Connect response object
4410      */
4411     /**
4412      * @event loadexception
4413      * Fires when loading fails.
4414      * @param {Roo.JsonView} this
4415      * @param {Object} response The raw Connect response object
4416      */
4417     this.addEvents({
4418         'beforerender' : true,
4419         'load' : true,
4420         'loadexception' : true
4421     });
4422 };
4423 Roo.extend(Roo.JsonView, Roo.View, {
4424     /**
4425      * @type {String} The root property in the loaded JSON object that contains the data
4426      */
4427     jsonRoot : "",
4428
4429     /**
4430      * Refreshes the view.
4431      */
4432     refresh : function(){
4433         this.clearSelections();
4434         this.el.update("");
4435         var html = [];
4436         var o = this.jsonData;
4437         if(o && o.length > 0){
4438             for(var i = 0, len = o.length; i < len; i++){
4439                 var data = this.prepareData(o[i], i, o);
4440                 html[html.length] = this.tpl.apply(data);
4441             }
4442         }else{
4443             html.push(this.emptyText);
4444         }
4445         this.el.update(html.join(""));
4446         this.nodes = this.el.dom.childNodes;
4447         this.updateIndexes(0);
4448     },
4449
4450     /**
4451      * 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.
4452      * @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:
4453      <pre><code>
4454      view.load({
4455          url: "your-url.php",
4456          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4457          callback: yourFunction,
4458          scope: yourObject, //(optional scope)
4459          discardUrl: false,
4460          nocache: false,
4461          text: "Loading...",
4462          timeout: 30,
4463          scripts: false
4464      });
4465      </code></pre>
4466      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4467      * 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.
4468      * @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}
4469      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4470      * @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.
4471      */
4472     load : function(){
4473         var um = this.el.getUpdateManager();
4474         um.update.apply(um, arguments);
4475     },
4476
4477     // note - render is a standard framework call...
4478     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4479     render : function(el, response){
4480         
4481         this.clearSelections();
4482         this.el.update("");
4483         var o;
4484         try{
4485             if (response != '') {
4486                 o = Roo.util.JSON.decode(response.responseText);
4487                 if(this.jsonRoot){
4488                     
4489                     o = o[this.jsonRoot];
4490                 }
4491             }
4492         } catch(e){
4493         }
4494         /**
4495          * The current JSON data or null
4496          */
4497         this.jsonData = o;
4498         this.beforeRender();
4499         this.refresh();
4500     },
4501
4502 /**
4503  * Get the number of records in the current JSON dataset
4504  * @return {Number}
4505  */
4506     getCount : function(){
4507         return this.jsonData ? this.jsonData.length : 0;
4508     },
4509
4510 /**
4511  * Returns the JSON object for the specified node(s)
4512  * @param {HTMLElement/Array} node The node or an array of nodes
4513  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4514  * you get the JSON object for the node
4515  */
4516     getNodeData : function(node){
4517         if(node instanceof Array){
4518             var data = [];
4519             for(var i = 0, len = node.length; i < len; i++){
4520                 data.push(this.getNodeData(node[i]));
4521             }
4522             return data;
4523         }
4524         return this.jsonData[this.indexOf(node)] || null;
4525     },
4526
4527     beforeRender : function(){
4528         this.snapshot = this.jsonData;
4529         if(this.sortInfo){
4530             this.sort.apply(this, this.sortInfo);
4531         }
4532         this.fireEvent("beforerender", this, this.jsonData);
4533     },
4534
4535     onLoad : function(el, o){
4536         this.fireEvent("load", this, this.jsonData, o);
4537     },
4538
4539     onLoadException : function(el, o){
4540         this.fireEvent("loadexception", this, o);
4541     },
4542
4543 /**
4544  * Filter the data by a specific property.
4545  * @param {String} property A property on your JSON objects
4546  * @param {String/RegExp} value Either string that the property values
4547  * should start with, or a RegExp to test against the property
4548  */
4549     filter : function(property, value){
4550         if(this.jsonData){
4551             var data = [];
4552             var ss = this.snapshot;
4553             if(typeof value == "string"){
4554                 var vlen = value.length;
4555                 if(vlen == 0){
4556                     this.clearFilter();
4557                     return;
4558                 }
4559                 value = value.toLowerCase();
4560                 for(var i = 0, len = ss.length; i < len; i++){
4561                     var o = ss[i];
4562                     if(o[property].substr(0, vlen).toLowerCase() == value){
4563                         data.push(o);
4564                     }
4565                 }
4566             } else if(value.exec){ // regex?
4567                 for(var i = 0, len = ss.length; i < len; i++){
4568                     var o = ss[i];
4569                     if(value.test(o[property])){
4570                         data.push(o);
4571                     }
4572                 }
4573             } else{
4574                 return;
4575             }
4576             this.jsonData = data;
4577             this.refresh();
4578         }
4579     },
4580
4581 /**
4582  * Filter by a function. The passed function will be called with each
4583  * object in the current dataset. If the function returns true the value is kept,
4584  * otherwise it is filtered.
4585  * @param {Function} fn
4586  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4587  */
4588     filterBy : function(fn, scope){
4589         if(this.jsonData){
4590             var data = [];
4591             var ss = this.snapshot;
4592             for(var i = 0, len = ss.length; i < len; i++){
4593                 var o = ss[i];
4594                 if(fn.call(scope || this, o)){
4595                     data.push(o);
4596                 }
4597             }
4598             this.jsonData = data;
4599             this.refresh();
4600         }
4601     },
4602
4603 /**
4604  * Clears the current filter.
4605  */
4606     clearFilter : function(){
4607         if(this.snapshot && this.jsonData != this.snapshot){
4608             this.jsonData = this.snapshot;
4609             this.refresh();
4610         }
4611     },
4612
4613
4614 /**
4615  * Sorts the data for this view and refreshes it.
4616  * @param {String} property A property on your JSON objects to sort on
4617  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4618  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4619  */
4620     sort : function(property, dir, sortType){
4621         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4622         if(this.jsonData){
4623             var p = property;
4624             var dsc = dir && dir.toLowerCase() == "desc";
4625             var f = function(o1, o2){
4626                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4627                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4628                 ;
4629                 if(v1 < v2){
4630                     return dsc ? +1 : -1;
4631                 } else if(v1 > v2){
4632                     return dsc ? -1 : +1;
4633                 } else{
4634                     return 0;
4635                 }
4636             };
4637             this.jsonData.sort(f);
4638             this.refresh();
4639             if(this.jsonData != this.snapshot){
4640                 this.snapshot.sort(f);
4641             }
4642         }
4643     }
4644 });/*
4645  * Based on:
4646  * Ext JS Library 1.1.1
4647  * Copyright(c) 2006-2007, Ext JS, LLC.
4648  *
4649  * Originally Released Under LGPL - original licence link has changed is not relivant.
4650  *
4651  * Fork - LGPL
4652  * <script type="text/javascript">
4653  */
4654  
4655
4656 /**
4657  * @class Roo.ColorPalette
4658  * @extends Roo.Component
4659  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4660  * Here's an example of typical usage:
4661  * <pre><code>
4662 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4663 cp.render('my-div');
4664
4665 cp.on('select', function(palette, selColor){
4666     // do something with selColor
4667 });
4668 </code></pre>
4669  * @constructor
4670  * Create a new ColorPalette
4671  * @param {Object} config The config object
4672  */
4673 Roo.ColorPalette = function(config){
4674     Roo.ColorPalette.superclass.constructor.call(this, config);
4675     this.addEvents({
4676         /**
4677              * @event select
4678              * Fires when a color is selected
4679              * @param {ColorPalette} this
4680              * @param {String} color The 6-digit color hex code (without the # symbol)
4681              */
4682         select: true
4683     });
4684
4685     if(this.handler){
4686         this.on("select", this.handler, this.scope, true);
4687     }
4688 };
4689 Roo.extend(Roo.ColorPalette, Roo.Component, {
4690     /**
4691      * @cfg {String} itemCls
4692      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4693      */
4694     itemCls : "x-color-palette",
4695     /**
4696      * @cfg {String} value
4697      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4698      * the hex codes are case-sensitive.
4699      */
4700     value : null,
4701     clickEvent:'click',
4702     // private
4703     ctype: "Roo.ColorPalette",
4704
4705     /**
4706      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4707      */
4708     allowReselect : false,
4709
4710     /**
4711      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4712      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4713      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4714      * of colors with the width setting until the box is symmetrical.</p>
4715      * <p>You can override individual colors if needed:</p>
4716      * <pre><code>
4717 var cp = new Roo.ColorPalette();
4718 cp.colors[0] = "FF0000";  // change the first box to red
4719 </code></pre>
4720
4721 Or you can provide a custom array of your own for complete control:
4722 <pre><code>
4723 var cp = new Roo.ColorPalette();
4724 cp.colors = ["000000", "993300", "333300"];
4725 </code></pre>
4726      * @type Array
4727      */
4728     colors : [
4729         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4730         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4731         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4732         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4733         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4734     ],
4735
4736     // private
4737     onRender : function(container, position){
4738         var t = new Roo.MasterTemplate(
4739             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4740         );
4741         var c = this.colors;
4742         for(var i = 0, len = c.length; i < len; i++){
4743             t.add([c[i]]);
4744         }
4745         var el = document.createElement("div");
4746         el.className = this.itemCls;
4747         t.overwrite(el);
4748         container.dom.insertBefore(el, position);
4749         this.el = Roo.get(el);
4750         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4751         if(this.clickEvent != 'click'){
4752             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4753         }
4754     },
4755
4756     // private
4757     afterRender : function(){
4758         Roo.ColorPalette.superclass.afterRender.call(this);
4759         if(this.value){
4760             var s = this.value;
4761             this.value = null;
4762             this.select(s);
4763         }
4764     },
4765
4766     // private
4767     handleClick : function(e, t){
4768         e.preventDefault();
4769         if(!this.disabled){
4770             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4771             this.select(c.toUpperCase());
4772         }
4773     },
4774
4775     /**
4776      * Selects the specified color in the palette (fires the select event)
4777      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4778      */
4779     select : function(color){
4780         color = color.replace("#", "");
4781         if(color != this.value || this.allowReselect){
4782             var el = this.el;
4783             if(this.value){
4784                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4785             }
4786             el.child("a.color-"+color).addClass("x-color-palette-sel");
4787             this.value = color;
4788             this.fireEvent("select", this, color);
4789         }
4790     }
4791 });/*
4792  * Based on:
4793  * Ext JS Library 1.1.1
4794  * Copyright(c) 2006-2007, Ext JS, LLC.
4795  *
4796  * Originally Released Under LGPL - original licence link has changed is not relivant.
4797  *
4798  * Fork - LGPL
4799  * <script type="text/javascript">
4800  */
4801  
4802 /**
4803  * @class Roo.DatePicker
4804  * @extends Roo.Component
4805  * Simple date picker class.
4806  * @constructor
4807  * Create a new DatePicker
4808  * @param {Object} config The config object
4809  */
4810 Roo.DatePicker = function(config){
4811     Roo.DatePicker.superclass.constructor.call(this, config);
4812
4813     this.value = config && config.value ?
4814                  config.value.clearTime() : new Date().clearTime();
4815
4816     this.addEvents({
4817         /**
4818              * @event select
4819              * Fires when a date is selected
4820              * @param {DatePicker} this
4821              * @param {Date} date The selected date
4822              */
4823         'select': true,
4824         /**
4825              * @event monthchange
4826              * Fires when the displayed month changes 
4827              * @param {DatePicker} this
4828              * @param {Date} date The selected month
4829              */
4830         'monthchange': true
4831     });
4832
4833     if(this.handler){
4834         this.on("select", this.handler,  this.scope || this);
4835     }
4836     // build the disabledDatesRE
4837     if(!this.disabledDatesRE && this.disabledDates){
4838         var dd = this.disabledDates;
4839         var re = "(?:";
4840         for(var i = 0; i < dd.length; i++){
4841             re += dd[i];
4842             if(i != dd.length-1) {
4843                 re += "|";
4844             }
4845         }
4846         this.disabledDatesRE = new RegExp(re + ")");
4847     }
4848 };
4849
4850 Roo.extend(Roo.DatePicker, Roo.Component, {
4851     /**
4852      * @cfg {String} todayText
4853      * The text to display on the button that selects the current date (defaults to "Today")
4854      */
4855     todayText : "Today",
4856     /**
4857      * @cfg {String} okText
4858      * The text to display on the ok button
4859      */
4860     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4861     /**
4862      * @cfg {String} cancelText
4863      * The text to display on the cancel button
4864      */
4865     cancelText : "Cancel",
4866     /**
4867      * @cfg {String} todayTip
4868      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4869      */
4870     todayTip : "{0} (Spacebar)",
4871     /**
4872      * @cfg {Date} minDate
4873      * Minimum allowable date (JavaScript date object, defaults to null)
4874      */
4875     minDate : null,
4876     /**
4877      * @cfg {Date} maxDate
4878      * Maximum allowable date (JavaScript date object, defaults to null)
4879      */
4880     maxDate : null,
4881     /**
4882      * @cfg {String} minText
4883      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4884      */
4885     minText : "This date is before the minimum date",
4886     /**
4887      * @cfg {String} maxText
4888      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4889      */
4890     maxText : "This date is after the maximum date",
4891     /**
4892      * @cfg {String} format
4893      * The default date format string which can be overriden for localization support.  The format must be
4894      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4895      */
4896     format : "m/d/y",
4897     /**
4898      * @cfg {Array} disabledDays
4899      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4900      */
4901     disabledDays : null,
4902     /**
4903      * @cfg {String} disabledDaysText
4904      * The tooltip to display when the date falls on a disabled day (defaults to "")
4905      */
4906     disabledDaysText : "",
4907     /**
4908      * @cfg {RegExp} disabledDatesRE
4909      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4910      */
4911     disabledDatesRE : null,
4912     /**
4913      * @cfg {String} disabledDatesText
4914      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4915      */
4916     disabledDatesText : "",
4917     /**
4918      * @cfg {Boolean} constrainToViewport
4919      * True to constrain the date picker to the viewport (defaults to true)
4920      */
4921     constrainToViewport : true,
4922     /**
4923      * @cfg {Array} monthNames
4924      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4925      */
4926     monthNames : Date.monthNames,
4927     /**
4928      * @cfg {Array} dayNames
4929      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4930      */
4931     dayNames : Date.dayNames,
4932     /**
4933      * @cfg {String} nextText
4934      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4935      */
4936     nextText: 'Next Month (Control+Right)',
4937     /**
4938      * @cfg {String} prevText
4939      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4940      */
4941     prevText: 'Previous Month (Control+Left)',
4942     /**
4943      * @cfg {String} monthYearText
4944      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4945      */
4946     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4947     /**
4948      * @cfg {Number} startDay
4949      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4950      */
4951     startDay : 0,
4952     /**
4953      * @cfg {Bool} showClear
4954      * Show a clear button (usefull for date form elements that can be blank.)
4955      */
4956     
4957     showClear: false,
4958     
4959     /**
4960      * Sets the value of the date field
4961      * @param {Date} value The date to set
4962      */
4963     setValue : function(value){
4964         var old = this.value;
4965         
4966         if (typeof(value) == 'string') {
4967          
4968             value = Date.parseDate(value, this.format);
4969         }
4970         if (!value) {
4971             value = new Date();
4972         }
4973         
4974         this.value = value.clearTime(true);
4975         if(this.el){
4976             this.update(this.value);
4977         }
4978     },
4979
4980     /**
4981      * Gets the current selected value of the date field
4982      * @return {Date} The selected date
4983      */
4984     getValue : function(){
4985         return this.value;
4986     },
4987
4988     // private
4989     focus : function(){
4990         if(this.el){
4991             this.update(this.activeDate);
4992         }
4993     },
4994
4995     // privateval
4996     onRender : function(container, position){
4997         
4998         var m = [
4999              '<table cellspacing="0">',
5000                 '<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>',
5001                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5002         var dn = this.dayNames;
5003         for(var i = 0; i < 7; i++){
5004             var d = this.startDay+i;
5005             if(d > 6){
5006                 d = d-7;
5007             }
5008             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5009         }
5010         m[m.length] = "</tr></thead><tbody><tr>";
5011         for(var i = 0; i < 42; i++) {
5012             if(i % 7 == 0 && i != 0){
5013                 m[m.length] = "</tr><tr>";
5014             }
5015             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5016         }
5017         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5018             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5019
5020         var el = document.createElement("div");
5021         el.className = "x-date-picker";
5022         el.innerHTML = m.join("");
5023
5024         container.dom.insertBefore(el, position);
5025
5026         this.el = Roo.get(el);
5027         this.eventEl = Roo.get(el.firstChild);
5028
5029         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5030             handler: this.showPrevMonth,
5031             scope: this,
5032             preventDefault:true,
5033             stopDefault:true
5034         });
5035
5036         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5037             handler: this.showNextMonth,
5038             scope: this,
5039             preventDefault:true,
5040             stopDefault:true
5041         });
5042
5043         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5044
5045         this.monthPicker = this.el.down('div.x-date-mp');
5046         this.monthPicker.enableDisplayMode('block');
5047         
5048         var kn = new Roo.KeyNav(this.eventEl, {
5049             "left" : function(e){
5050                 e.ctrlKey ?
5051                     this.showPrevMonth() :
5052                     this.update(this.activeDate.add("d", -1));
5053             },
5054
5055             "right" : function(e){
5056                 e.ctrlKey ?
5057                     this.showNextMonth() :
5058                     this.update(this.activeDate.add("d", 1));
5059             },
5060
5061             "up" : function(e){
5062                 e.ctrlKey ?
5063                     this.showNextYear() :
5064                     this.update(this.activeDate.add("d", -7));
5065             },
5066
5067             "down" : function(e){
5068                 e.ctrlKey ?
5069                     this.showPrevYear() :
5070                     this.update(this.activeDate.add("d", 7));
5071             },
5072
5073             "pageUp" : function(e){
5074                 this.showNextMonth();
5075             },
5076
5077             "pageDown" : function(e){
5078                 this.showPrevMonth();
5079             },
5080
5081             "enter" : function(e){
5082                 e.stopPropagation();
5083                 return true;
5084             },
5085
5086             scope : this
5087         });
5088
5089         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5090
5091         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5092
5093         this.el.unselectable();
5094         
5095         this.cells = this.el.select("table.x-date-inner tbody td");
5096         this.textNodes = this.el.query("table.x-date-inner tbody span");
5097
5098         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5099             text: "&#160;",
5100             tooltip: this.monthYearText
5101         });
5102
5103         this.mbtn.on('click', this.showMonthPicker, this);
5104         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5105
5106
5107         var today = (new Date()).dateFormat(this.format);
5108         
5109         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5110         if (this.showClear) {
5111             baseTb.add( new Roo.Toolbar.Fill());
5112         }
5113         baseTb.add({
5114             text: String.format(this.todayText, today),
5115             tooltip: String.format(this.todayTip, today),
5116             handler: this.selectToday,
5117             scope: this
5118         });
5119         
5120         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5121             
5122         //});
5123         if (this.showClear) {
5124             
5125             baseTb.add( new Roo.Toolbar.Fill());
5126             baseTb.add({
5127                 text: '&#160;',
5128                 cls: 'x-btn-icon x-btn-clear',
5129                 handler: function() {
5130                     //this.value = '';
5131                     this.fireEvent("select", this, '');
5132                 },
5133                 scope: this
5134             });
5135         }
5136         
5137         
5138         if(Roo.isIE){
5139             this.el.repaint();
5140         }
5141         this.update(this.value);
5142     },
5143
5144     createMonthPicker : function(){
5145         if(!this.monthPicker.dom.firstChild){
5146             var buf = ['<table border="0" cellspacing="0">'];
5147             for(var i = 0; i < 6; i++){
5148                 buf.push(
5149                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5150                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5151                     i == 0 ?
5152                     '<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>' :
5153                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5154                 );
5155             }
5156             buf.push(
5157                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5158                     this.okText,
5159                     '</button><button type="button" class="x-date-mp-cancel">',
5160                     this.cancelText,
5161                     '</button></td></tr>',
5162                 '</table>'
5163             );
5164             this.monthPicker.update(buf.join(''));
5165             this.monthPicker.on('click', this.onMonthClick, this);
5166             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5167
5168             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5169             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5170
5171             this.mpMonths.each(function(m, a, i){
5172                 i += 1;
5173                 if((i%2) == 0){
5174                     m.dom.xmonth = 5 + Math.round(i * .5);
5175                 }else{
5176                     m.dom.xmonth = Math.round((i-1) * .5);
5177                 }
5178             });
5179         }
5180     },
5181
5182     showMonthPicker : function(){
5183         this.createMonthPicker();
5184         var size = this.el.getSize();
5185         this.monthPicker.setSize(size);
5186         this.monthPicker.child('table').setSize(size);
5187
5188         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5189         this.updateMPMonth(this.mpSelMonth);
5190         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5191         this.updateMPYear(this.mpSelYear);
5192
5193         this.monthPicker.slideIn('t', {duration:.2});
5194     },
5195
5196     updateMPYear : function(y){
5197         this.mpyear = y;
5198         var ys = this.mpYears.elements;
5199         for(var i = 1; i <= 10; i++){
5200             var td = ys[i-1], y2;
5201             if((i%2) == 0){
5202                 y2 = y + Math.round(i * .5);
5203                 td.firstChild.innerHTML = y2;
5204                 td.xyear = y2;
5205             }else{
5206                 y2 = y - (5-Math.round(i * .5));
5207                 td.firstChild.innerHTML = y2;
5208                 td.xyear = y2;
5209             }
5210             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5211         }
5212     },
5213
5214     updateMPMonth : function(sm){
5215         this.mpMonths.each(function(m, a, i){
5216             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5217         });
5218     },
5219
5220     selectMPMonth: function(m){
5221         
5222     },
5223
5224     onMonthClick : function(e, t){
5225         e.stopEvent();
5226         var el = new Roo.Element(t), pn;
5227         if(el.is('button.x-date-mp-cancel')){
5228             this.hideMonthPicker();
5229         }
5230         else if(el.is('button.x-date-mp-ok')){
5231             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5232             this.hideMonthPicker();
5233         }
5234         else if(pn = el.up('td.x-date-mp-month', 2)){
5235             this.mpMonths.removeClass('x-date-mp-sel');
5236             pn.addClass('x-date-mp-sel');
5237             this.mpSelMonth = pn.dom.xmonth;
5238         }
5239         else if(pn = el.up('td.x-date-mp-year', 2)){
5240             this.mpYears.removeClass('x-date-mp-sel');
5241             pn.addClass('x-date-mp-sel');
5242             this.mpSelYear = pn.dom.xyear;
5243         }
5244         else if(el.is('a.x-date-mp-prev')){
5245             this.updateMPYear(this.mpyear-10);
5246         }
5247         else if(el.is('a.x-date-mp-next')){
5248             this.updateMPYear(this.mpyear+10);
5249         }
5250     },
5251
5252     onMonthDblClick : function(e, t){
5253         e.stopEvent();
5254         var el = new Roo.Element(t), pn;
5255         if(pn = el.up('td.x-date-mp-month', 2)){
5256             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5257             this.hideMonthPicker();
5258         }
5259         else if(pn = el.up('td.x-date-mp-year', 2)){
5260             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5261             this.hideMonthPicker();
5262         }
5263     },
5264
5265     hideMonthPicker : function(disableAnim){
5266         if(this.monthPicker){
5267             if(disableAnim === true){
5268                 this.monthPicker.hide();
5269             }else{
5270                 this.monthPicker.slideOut('t', {duration:.2});
5271             }
5272         }
5273     },
5274
5275     // private
5276     showPrevMonth : function(e){
5277         this.update(this.activeDate.add("mo", -1));
5278     },
5279
5280     // private
5281     showNextMonth : function(e){
5282         this.update(this.activeDate.add("mo", 1));
5283     },
5284
5285     // private
5286     showPrevYear : function(){
5287         this.update(this.activeDate.add("y", -1));
5288     },
5289
5290     // private
5291     showNextYear : function(){
5292         this.update(this.activeDate.add("y", 1));
5293     },
5294
5295     // private
5296     handleMouseWheel : function(e){
5297         var delta = e.getWheelDelta();
5298         if(delta > 0){
5299             this.showPrevMonth();
5300             e.stopEvent();
5301         } else if(delta < 0){
5302             this.showNextMonth();
5303             e.stopEvent();
5304         }
5305     },
5306
5307     // private
5308     handleDateClick : function(e, t){
5309         e.stopEvent();
5310         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5311             this.setValue(new Date(t.dateValue));
5312             this.fireEvent("select", this, this.value);
5313         }
5314     },
5315
5316     // private
5317     selectToday : function(){
5318         this.setValue(new Date().clearTime());
5319         this.fireEvent("select", this, this.value);
5320     },
5321
5322     // private
5323     update : function(date)
5324     {
5325         var vd = this.activeDate;
5326         this.activeDate = date;
5327         if(vd && this.el){
5328             var t = date.getTime();
5329             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5330                 this.cells.removeClass("x-date-selected");
5331                 this.cells.each(function(c){
5332                    if(c.dom.firstChild.dateValue == t){
5333                        c.addClass("x-date-selected");
5334                        setTimeout(function(){
5335                             try{c.dom.firstChild.focus();}catch(e){}
5336                        }, 50);
5337                        return false;
5338                    }
5339                 });
5340                 return;
5341             }
5342         }
5343         
5344         var days = date.getDaysInMonth();
5345         var firstOfMonth = date.getFirstDateOfMonth();
5346         var startingPos = firstOfMonth.getDay()-this.startDay;
5347
5348         if(startingPos <= this.startDay){
5349             startingPos += 7;
5350         }
5351
5352         var pm = date.add("mo", -1);
5353         var prevStart = pm.getDaysInMonth()-startingPos;
5354
5355         var cells = this.cells.elements;
5356         var textEls = this.textNodes;
5357         days += startingPos;
5358
5359         // convert everything to numbers so it's fast
5360         var day = 86400000;
5361         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5362         var today = new Date().clearTime().getTime();
5363         var sel = date.clearTime().getTime();
5364         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5365         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5366         var ddMatch = this.disabledDatesRE;
5367         var ddText = this.disabledDatesText;
5368         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5369         var ddaysText = this.disabledDaysText;
5370         var format = this.format;
5371
5372         var setCellClass = function(cal, cell){
5373             cell.title = "";
5374             var t = d.getTime();
5375             cell.firstChild.dateValue = t;
5376             if(t == today){
5377                 cell.className += " x-date-today";
5378                 cell.title = cal.todayText;
5379             }
5380             if(t == sel){
5381                 cell.className += " x-date-selected";
5382                 setTimeout(function(){
5383                     try{cell.firstChild.focus();}catch(e){}
5384                 }, 50);
5385             }
5386             // disabling
5387             if(t < min) {
5388                 cell.className = " x-date-disabled";
5389                 cell.title = cal.minText;
5390                 return;
5391             }
5392             if(t > max) {
5393                 cell.className = " x-date-disabled";
5394                 cell.title = cal.maxText;
5395                 return;
5396             }
5397             if(ddays){
5398                 if(ddays.indexOf(d.getDay()) != -1){
5399                     cell.title = ddaysText;
5400                     cell.className = " x-date-disabled";
5401                 }
5402             }
5403             if(ddMatch && format){
5404                 var fvalue = d.dateFormat(format);
5405                 if(ddMatch.test(fvalue)){
5406                     cell.title = ddText.replace("%0", fvalue);
5407                     cell.className = " x-date-disabled";
5408                 }
5409             }
5410         };
5411
5412         var i = 0;
5413         for(; i < startingPos; i++) {
5414             textEls[i].innerHTML = (++prevStart);
5415             d.setDate(d.getDate()+1);
5416             cells[i].className = "x-date-prevday";
5417             setCellClass(this, cells[i]);
5418         }
5419         for(; i < days; i++){
5420             intDay = i - startingPos + 1;
5421             textEls[i].innerHTML = (intDay);
5422             d.setDate(d.getDate()+1);
5423             cells[i].className = "x-date-active";
5424             setCellClass(this, cells[i]);
5425         }
5426         var extraDays = 0;
5427         for(; i < 42; i++) {
5428              textEls[i].innerHTML = (++extraDays);
5429              d.setDate(d.getDate()+1);
5430              cells[i].className = "x-date-nextday";
5431              setCellClass(this, cells[i]);
5432         }
5433
5434         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5435         this.fireEvent('monthchange', this, date);
5436         
5437         if(!this.internalRender){
5438             var main = this.el.dom.firstChild;
5439             var w = main.offsetWidth;
5440             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5441             Roo.fly(main).setWidth(w);
5442             this.internalRender = true;
5443             // opera does not respect the auto grow header center column
5444             // then, after it gets a width opera refuses to recalculate
5445             // without a second pass
5446             if(Roo.isOpera && !this.secondPass){
5447                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5448                 this.secondPass = true;
5449                 this.update.defer(10, this, [date]);
5450             }
5451         }
5452         
5453         
5454     }
5455 });        /*
5456  * Based on:
5457  * Ext JS Library 1.1.1
5458  * Copyright(c) 2006-2007, Ext JS, LLC.
5459  *
5460  * Originally Released Under LGPL - original licence link has changed is not relivant.
5461  *
5462  * Fork - LGPL
5463  * <script type="text/javascript">
5464  */
5465 /**
5466  * @class Roo.TabPanel
5467  * @extends Roo.util.Observable
5468  * A lightweight tab container.
5469  * <br><br>
5470  * Usage:
5471  * <pre><code>
5472 // basic tabs 1, built from existing content
5473 var tabs = new Roo.TabPanel("tabs1");
5474 tabs.addTab("script", "View Script");
5475 tabs.addTab("markup", "View Markup");
5476 tabs.activate("script");
5477
5478 // more advanced tabs, built from javascript
5479 var jtabs = new Roo.TabPanel("jtabs");
5480 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5481
5482 // set up the UpdateManager
5483 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5484 var updater = tab2.getUpdateManager();
5485 updater.setDefaultUrl("ajax1.htm");
5486 tab2.on('activate', updater.refresh, updater, true);
5487
5488 // Use setUrl for Ajax loading
5489 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5490 tab3.setUrl("ajax2.htm", null, true);
5491
5492 // Disabled tab
5493 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5494 tab4.disable();
5495
5496 jtabs.activate("jtabs-1");
5497  * </code></pre>
5498  * @constructor
5499  * Create a new TabPanel.
5500  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5501  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5502  */
5503 Roo.TabPanel = function(container, config){
5504     /**
5505     * The container element for this TabPanel.
5506     * @type Roo.Element
5507     */
5508     this.el = Roo.get(container, true);
5509     if(config){
5510         if(typeof config == "boolean"){
5511             this.tabPosition = config ? "bottom" : "top";
5512         }else{
5513             Roo.apply(this, config);
5514         }
5515     }
5516     if(this.tabPosition == "bottom"){
5517         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5518         this.el.addClass("x-tabs-bottom");
5519     }
5520     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5521     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5522     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5523     if(Roo.isIE){
5524         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5525     }
5526     if(this.tabPosition != "bottom"){
5527         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5528          * @type Roo.Element
5529          */
5530         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5531         this.el.addClass("x-tabs-top");
5532     }
5533     this.items = [];
5534
5535     this.bodyEl.setStyle("position", "relative");
5536
5537     this.active = null;
5538     this.activateDelegate = this.activate.createDelegate(this);
5539
5540     this.addEvents({
5541         /**
5542          * @event tabchange
5543          * Fires when the active tab changes
5544          * @param {Roo.TabPanel} this
5545          * @param {Roo.TabPanelItem} activePanel The new active tab
5546          */
5547         "tabchange": true,
5548         /**
5549          * @event beforetabchange
5550          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5551          * @param {Roo.TabPanel} this
5552          * @param {Object} e Set cancel to true on this object to cancel the tab change
5553          * @param {Roo.TabPanelItem} tab The tab being changed to
5554          */
5555         "beforetabchange" : true
5556     });
5557
5558     Roo.EventManager.onWindowResize(this.onResize, this);
5559     this.cpad = this.el.getPadding("lr");
5560     this.hiddenCount = 0;
5561
5562
5563     // toolbar on the tabbar support...
5564     if (this.toolbar) {
5565         var tcfg = this.toolbar;
5566         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5567         this.toolbar = new Roo.Toolbar(tcfg);
5568         if (Roo.isSafari) {
5569             var tbl = tcfg.container.child('table', true);
5570             tbl.setAttribute('width', '100%');
5571         }
5572         
5573     }
5574    
5575
5576
5577     Roo.TabPanel.superclass.constructor.call(this);
5578 };
5579
5580 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5581     /*
5582      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5583      */
5584     tabPosition : "top",
5585     /*
5586      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5587      */
5588     currentTabWidth : 0,
5589     /*
5590      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5591      */
5592     minTabWidth : 40,
5593     /*
5594      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5595      */
5596     maxTabWidth : 250,
5597     /*
5598      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5599      */
5600     preferredTabWidth : 175,
5601     /*
5602      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5603      */
5604     resizeTabs : false,
5605     /*
5606      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5607      */
5608     monitorResize : true,
5609     /*
5610      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5611      */
5612     toolbar : false,
5613
5614     /**
5615      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5616      * @param {String} id The id of the div to use <b>or create</b>
5617      * @param {String} text The text for the tab
5618      * @param {String} content (optional) Content to put in the TabPanelItem body
5619      * @param {Boolean} closable (optional) True to create a close icon on the tab
5620      * @return {Roo.TabPanelItem} The created TabPanelItem
5621      */
5622     addTab : function(id, text, content, closable){
5623         var item = new Roo.TabPanelItem(this, id, text, closable);
5624         this.addTabItem(item);
5625         if(content){
5626             item.setContent(content);
5627         }
5628         return item;
5629     },
5630
5631     /**
5632      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5633      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5634      * @return {Roo.TabPanelItem}
5635      */
5636     getTab : function(id){
5637         return this.items[id];
5638     },
5639
5640     /**
5641      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5642      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5643      */
5644     hideTab : function(id){
5645         var t = this.items[id];
5646         if(!t.isHidden()){
5647            t.setHidden(true);
5648            this.hiddenCount++;
5649            this.autoSizeTabs();
5650         }
5651     },
5652
5653     /**
5654      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5655      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5656      */
5657     unhideTab : function(id){
5658         var t = this.items[id];
5659         if(t.isHidden()){
5660            t.setHidden(false);
5661            this.hiddenCount--;
5662            this.autoSizeTabs();
5663         }
5664     },
5665
5666     /**
5667      * Adds an existing {@link Roo.TabPanelItem}.
5668      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5669      */
5670     addTabItem : function(item){
5671         this.items[item.id] = item;
5672         this.items.push(item);
5673         if(this.resizeTabs){
5674            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5675            this.autoSizeTabs();
5676         }else{
5677             item.autoSize();
5678         }
5679     },
5680
5681     /**
5682      * Removes a {@link Roo.TabPanelItem}.
5683      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5684      */
5685     removeTab : function(id){
5686         var items = this.items;
5687         var tab = items[id];
5688         if(!tab) { return; }
5689         var index = items.indexOf(tab);
5690         if(this.active == tab && items.length > 1){
5691             var newTab = this.getNextAvailable(index);
5692             if(newTab) {
5693                 newTab.activate();
5694             }
5695         }
5696         this.stripEl.dom.removeChild(tab.pnode.dom);
5697         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5698             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5699         }
5700         items.splice(index, 1);
5701         delete this.items[tab.id];
5702         tab.fireEvent("close", tab);
5703         tab.purgeListeners();
5704         this.autoSizeTabs();
5705     },
5706
5707     getNextAvailable : function(start){
5708         var items = this.items;
5709         var index = start;
5710         // look for a next tab that will slide over to
5711         // replace the one being removed
5712         while(index < items.length){
5713             var item = items[++index];
5714             if(item && !item.isHidden()){
5715                 return item;
5716             }
5717         }
5718         // if one isn't found select the previous tab (on the left)
5719         index = start;
5720         while(index >= 0){
5721             var item = items[--index];
5722             if(item && !item.isHidden()){
5723                 return item;
5724             }
5725         }
5726         return null;
5727     },
5728
5729     /**
5730      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5731      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5732      */
5733     disableTab : function(id){
5734         var tab = this.items[id];
5735         if(tab && this.active != tab){
5736             tab.disable();
5737         }
5738     },
5739
5740     /**
5741      * Enables a {@link Roo.TabPanelItem} that is disabled.
5742      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5743      */
5744     enableTab : function(id){
5745         var tab = this.items[id];
5746         tab.enable();
5747     },
5748
5749     /**
5750      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5751      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5752      * @return {Roo.TabPanelItem} The TabPanelItem.
5753      */
5754     activate : function(id){
5755         var tab = this.items[id];
5756         if(!tab){
5757             return null;
5758         }
5759         if(tab == this.active || tab.disabled){
5760             return tab;
5761         }
5762         var e = {};
5763         this.fireEvent("beforetabchange", this, e, tab);
5764         if(e.cancel !== true && !tab.disabled){
5765             if(this.active){
5766                 this.active.hide();
5767             }
5768             this.active = this.items[id];
5769             this.active.show();
5770             this.fireEvent("tabchange", this, this.active);
5771         }
5772         return tab;
5773     },
5774
5775     /**
5776      * Gets the active {@link Roo.TabPanelItem}.
5777      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5778      */
5779     getActiveTab : function(){
5780         return this.active;
5781     },
5782
5783     /**
5784      * Updates the tab body element to fit the height of the container element
5785      * for overflow scrolling
5786      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5787      */
5788     syncHeight : function(targetHeight){
5789         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5790         var bm = this.bodyEl.getMargins();
5791         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5792         this.bodyEl.setHeight(newHeight);
5793         return newHeight;
5794     },
5795
5796     onResize : function(){
5797         if(this.monitorResize){
5798             this.autoSizeTabs();
5799         }
5800     },
5801
5802     /**
5803      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5804      */
5805     beginUpdate : function(){
5806         this.updating = true;
5807     },
5808
5809     /**
5810      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5811      */
5812     endUpdate : function(){
5813         this.updating = false;
5814         this.autoSizeTabs();
5815     },
5816
5817     /**
5818      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5819      */
5820     autoSizeTabs : function(){
5821         var count = this.items.length;
5822         var vcount = count - this.hiddenCount;
5823         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5824             return;
5825         }
5826         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5827         var availWidth = Math.floor(w / vcount);
5828         var b = this.stripBody;
5829         if(b.getWidth() > w){
5830             var tabs = this.items;
5831             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5832             if(availWidth < this.minTabWidth){
5833                 /*if(!this.sleft){    // incomplete scrolling code
5834                     this.createScrollButtons();
5835                 }
5836                 this.showScroll();
5837                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5838             }
5839         }else{
5840             if(this.currentTabWidth < this.preferredTabWidth){
5841                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5842             }
5843         }
5844     },
5845
5846     /**
5847      * Returns the number of tabs in this TabPanel.
5848      * @return {Number}
5849      */
5850      getCount : function(){
5851          return this.items.length;
5852      },
5853
5854     /**
5855      * Resizes all the tabs to the passed width
5856      * @param {Number} The new width
5857      */
5858     setTabWidth : function(width){
5859         this.currentTabWidth = width;
5860         for(var i = 0, len = this.items.length; i < len; i++) {
5861                 if(!this.items[i].isHidden()) {
5862                 this.items[i].setWidth(width);
5863             }
5864         }
5865     },
5866
5867     /**
5868      * Destroys this TabPanel
5869      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5870      */
5871     destroy : function(removeEl){
5872         Roo.EventManager.removeResizeListener(this.onResize, this);
5873         for(var i = 0, len = this.items.length; i < len; i++){
5874             this.items[i].purgeListeners();
5875         }
5876         if(removeEl === true){
5877             this.el.update("");
5878             this.el.remove();
5879         }
5880     }
5881 });
5882
5883 /**
5884  * @class Roo.TabPanelItem
5885  * @extends Roo.util.Observable
5886  * Represents an individual item (tab plus body) in a TabPanel.
5887  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5888  * @param {String} id The id of this TabPanelItem
5889  * @param {String} text The text for the tab of this TabPanelItem
5890  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5891  */
5892 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5893     /**
5894      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5895      * @type Roo.TabPanel
5896      */
5897     this.tabPanel = tabPanel;
5898     /**
5899      * The id for this TabPanelItem
5900      * @type String
5901      */
5902     this.id = id;
5903     /** @private */
5904     this.disabled = false;
5905     /** @private */
5906     this.text = text;
5907     /** @private */
5908     this.loaded = false;
5909     this.closable = closable;
5910
5911     /**
5912      * The body element for this TabPanelItem.
5913      * @type Roo.Element
5914      */
5915     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5916     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5917     this.bodyEl.setStyle("display", "block");
5918     this.bodyEl.setStyle("zoom", "1");
5919     this.hideAction();
5920
5921     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5922     /** @private */
5923     this.el = Roo.get(els.el, true);
5924     this.inner = Roo.get(els.inner, true);
5925     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5926     this.pnode = Roo.get(els.el.parentNode, true);
5927     this.el.on("mousedown", this.onTabMouseDown, this);
5928     this.el.on("click", this.onTabClick, this);
5929     /** @private */
5930     if(closable){
5931         var c = Roo.get(els.close, true);
5932         c.dom.title = this.closeText;
5933         c.addClassOnOver("close-over");
5934         c.on("click", this.closeClick, this);
5935      }
5936
5937     this.addEvents({
5938          /**
5939          * @event activate
5940          * Fires when this tab becomes the active tab.
5941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5942          * @param {Roo.TabPanelItem} this
5943          */
5944         "activate": true,
5945         /**
5946          * @event beforeclose
5947          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5948          * @param {Roo.TabPanelItem} this
5949          * @param {Object} e Set cancel to true on this object to cancel the close.
5950          */
5951         "beforeclose": true,
5952         /**
5953          * @event close
5954          * Fires when this tab is closed.
5955          * @param {Roo.TabPanelItem} this
5956          */
5957          "close": true,
5958         /**
5959          * @event deactivate
5960          * Fires when this tab is no longer the active tab.
5961          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5962          * @param {Roo.TabPanelItem} this
5963          */
5964          "deactivate" : true
5965     });
5966     this.hidden = false;
5967
5968     Roo.TabPanelItem.superclass.constructor.call(this);
5969 };
5970
5971 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5972     purgeListeners : function(){
5973        Roo.util.Observable.prototype.purgeListeners.call(this);
5974        this.el.removeAllListeners();
5975     },
5976     /**
5977      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5978      */
5979     show : function(){
5980         this.pnode.addClass("on");
5981         this.showAction();
5982         if(Roo.isOpera){
5983             this.tabPanel.stripWrap.repaint();
5984         }
5985         this.fireEvent("activate", this.tabPanel, this);
5986     },
5987
5988     /**
5989      * Returns true if this tab is the active tab.
5990      * @return {Boolean}
5991      */
5992     isActive : function(){
5993         return this.tabPanel.getActiveTab() == this;
5994     },
5995
5996     /**
5997      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5998      */
5999     hide : function(){
6000         this.pnode.removeClass("on");
6001         this.hideAction();
6002         this.fireEvent("deactivate", this.tabPanel, this);
6003     },
6004
6005     hideAction : function(){
6006         this.bodyEl.hide();
6007         this.bodyEl.setStyle("position", "absolute");
6008         this.bodyEl.setLeft("-20000px");
6009         this.bodyEl.setTop("-20000px");
6010     },
6011
6012     showAction : function(){
6013         this.bodyEl.setStyle("position", "relative");
6014         this.bodyEl.setTop("");
6015         this.bodyEl.setLeft("");
6016         this.bodyEl.show();
6017     },
6018
6019     /**
6020      * Set the tooltip for the tab.
6021      * @param {String} tooltip The tab's tooltip
6022      */
6023     setTooltip : function(text){
6024         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6025             this.textEl.dom.qtip = text;
6026             this.textEl.dom.removeAttribute('title');
6027         }else{
6028             this.textEl.dom.title = text;
6029         }
6030     },
6031
6032     onTabClick : function(e){
6033         e.preventDefault();
6034         this.tabPanel.activate(this.id);
6035     },
6036
6037     onTabMouseDown : function(e){
6038         e.preventDefault();
6039         this.tabPanel.activate(this.id);
6040     },
6041
6042     getWidth : function(){
6043         return this.inner.getWidth();
6044     },
6045
6046     setWidth : function(width){
6047         var iwidth = width - this.pnode.getPadding("lr");
6048         this.inner.setWidth(iwidth);
6049         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6050         this.pnode.setWidth(width);
6051     },
6052
6053     /**
6054      * Show or hide the tab
6055      * @param {Boolean} hidden True to hide or false to show.
6056      */
6057     setHidden : function(hidden){
6058         this.hidden = hidden;
6059         this.pnode.setStyle("display", hidden ? "none" : "");
6060     },
6061
6062     /**
6063      * Returns true if this tab is "hidden"
6064      * @return {Boolean}
6065      */
6066     isHidden : function(){
6067         return this.hidden;
6068     },
6069
6070     /**
6071      * Returns the text for this tab
6072      * @return {String}
6073      */
6074     getText : function(){
6075         return this.text;
6076     },
6077
6078     autoSize : function(){
6079         //this.el.beginMeasure();
6080         this.textEl.setWidth(1);
6081         /*
6082          *  #2804 [new] Tabs in Roojs
6083          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6084          */
6085         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6086         //this.el.endMeasure();
6087     },
6088
6089     /**
6090      * Sets the text for the tab (Note: this also sets the tooltip text)
6091      * @param {String} text The tab's text and tooltip
6092      */
6093     setText : function(text){
6094         this.text = text;
6095         this.textEl.update(text);
6096         this.setTooltip(text);
6097         if(!this.tabPanel.resizeTabs){
6098             this.autoSize();
6099         }
6100     },
6101     /**
6102      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6103      */
6104     activate : function(){
6105         this.tabPanel.activate(this.id);
6106     },
6107
6108     /**
6109      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6110      */
6111     disable : function(){
6112         if(this.tabPanel.active != this){
6113             this.disabled = true;
6114             this.pnode.addClass("disabled");
6115         }
6116     },
6117
6118     /**
6119      * Enables this TabPanelItem if it was previously disabled.
6120      */
6121     enable : function(){
6122         this.disabled = false;
6123         this.pnode.removeClass("disabled");
6124     },
6125
6126     /**
6127      * Sets the content for this TabPanelItem.
6128      * @param {String} content The content
6129      * @param {Boolean} loadScripts true to look for and load scripts
6130      */
6131     setContent : function(content, loadScripts){
6132         this.bodyEl.update(content, loadScripts);
6133     },
6134
6135     /**
6136      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6137      * @return {Roo.UpdateManager} The UpdateManager
6138      */
6139     getUpdateManager : function(){
6140         return this.bodyEl.getUpdateManager();
6141     },
6142
6143     /**
6144      * Set a URL to be used to load the content for this TabPanelItem.
6145      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6146      * @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)
6147      * @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)
6148      * @return {Roo.UpdateManager} The UpdateManager
6149      */
6150     setUrl : function(url, params, loadOnce){
6151         if(this.refreshDelegate){
6152             this.un('activate', this.refreshDelegate);
6153         }
6154         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6155         this.on("activate", this.refreshDelegate);
6156         return this.bodyEl.getUpdateManager();
6157     },
6158
6159     /** @private */
6160     _handleRefresh : function(url, params, loadOnce){
6161         if(!loadOnce || !this.loaded){
6162             var updater = this.bodyEl.getUpdateManager();
6163             updater.update(url, params, this._setLoaded.createDelegate(this));
6164         }
6165     },
6166
6167     /**
6168      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6169      *   Will fail silently if the setUrl method has not been called.
6170      *   This does not activate the panel, just updates its content.
6171      */
6172     refresh : function(){
6173         if(this.refreshDelegate){
6174            this.loaded = false;
6175            this.refreshDelegate();
6176         }
6177     },
6178
6179     /** @private */
6180     _setLoaded : function(){
6181         this.loaded = true;
6182     },
6183
6184     /** @private */
6185     closeClick : function(e){
6186         var o = {};
6187         e.stopEvent();
6188         this.fireEvent("beforeclose", this, o);
6189         if(o.cancel !== true){
6190             this.tabPanel.removeTab(this.id);
6191         }
6192     },
6193     /**
6194      * The text displayed in the tooltip for the close icon.
6195      * @type String
6196      */
6197     closeText : "Close this tab"
6198 });
6199
6200 /** @private */
6201 Roo.TabPanel.prototype.createStrip = function(container){
6202     var strip = document.createElement("div");
6203     strip.className = "x-tabs-wrap";
6204     container.appendChild(strip);
6205     return strip;
6206 };
6207 /** @private */
6208 Roo.TabPanel.prototype.createStripList = function(strip){
6209     // div wrapper for retard IE
6210     // returns the "tr" element.
6211     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6212         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6213         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6214     return strip.firstChild.firstChild.firstChild.firstChild;
6215 };
6216 /** @private */
6217 Roo.TabPanel.prototype.createBody = function(container){
6218     var body = document.createElement("div");
6219     Roo.id(body, "tab-body");
6220     Roo.fly(body).addClass("x-tabs-body");
6221     container.appendChild(body);
6222     return body;
6223 };
6224 /** @private */
6225 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6226     var body = Roo.getDom(id);
6227     if(!body){
6228         body = document.createElement("div");
6229         body.id = id;
6230     }
6231     Roo.fly(body).addClass("x-tabs-item-body");
6232     bodyEl.insertBefore(body, bodyEl.firstChild);
6233     return body;
6234 };
6235 /** @private */
6236 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6237     var td = document.createElement("td");
6238     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6239     //stripEl.appendChild(td);
6240     if(closable){
6241         td.className = "x-tabs-closable";
6242         if(!this.closeTpl){
6243             this.closeTpl = new Roo.Template(
6244                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6245                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6246                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6247             );
6248         }
6249         var el = this.closeTpl.overwrite(td, {"text": text});
6250         var close = el.getElementsByTagName("div")[0];
6251         var inner = el.getElementsByTagName("em")[0];
6252         return {"el": el, "close": close, "inner": inner};
6253     } else {
6254         if(!this.tabTpl){
6255             this.tabTpl = new Roo.Template(
6256                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6257                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6258             );
6259         }
6260         var el = this.tabTpl.overwrite(td, {"text": text});
6261         var inner = el.getElementsByTagName("em")[0];
6262         return {"el": el, "inner": inner};
6263     }
6264 };/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.Button
6277  * @extends Roo.util.Observable
6278  * Simple Button class
6279  * @cfg {String} text The button text
6280  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6281  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6282  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6283  * @cfg {Object} scope The scope of the handler
6284  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6285  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6286  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6287  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6288  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6289  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6290    applies if enableToggle = true)
6291  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6292  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6293   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6294  * @constructor
6295  * Create a new button
6296  * @param {Object} config The config object
6297  */
6298 Roo.Button = function(renderTo, config)
6299 {
6300     if (!config) {
6301         config = renderTo;
6302         renderTo = config.renderTo || false;
6303     }
6304     
6305     Roo.apply(this, config);
6306     this.addEvents({
6307         /**
6308              * @event click
6309              * Fires when this button is clicked
6310              * @param {Button} this
6311              * @param {EventObject} e The click event
6312              */
6313             "click" : true,
6314         /**
6315              * @event toggle
6316              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6317              * @param {Button} this
6318              * @param {Boolean} pressed
6319              */
6320             "toggle" : true,
6321         /**
6322              * @event mouseover
6323              * Fires when the mouse hovers over the button
6324              * @param {Button} this
6325              * @param {Event} e The event object
6326              */
6327         'mouseover' : true,
6328         /**
6329              * @event mouseout
6330              * Fires when the mouse exits the button
6331              * @param {Button} this
6332              * @param {Event} e The event object
6333              */
6334         'mouseout': true,
6335          /**
6336              * @event render
6337              * Fires when the button is rendered
6338              * @param {Button} this
6339              */
6340         'render': true
6341     });
6342     if(this.menu){
6343         this.menu = Roo.menu.MenuMgr.get(this.menu);
6344     }
6345     // register listeners first!!  - so render can be captured..
6346     Roo.util.Observable.call(this);
6347     if(renderTo){
6348         this.render(renderTo);
6349     }
6350     
6351   
6352 };
6353
6354 Roo.extend(Roo.Button, Roo.util.Observable, {
6355     /**
6356      * 
6357      */
6358     
6359     /**
6360      * Read-only. True if this button is hidden
6361      * @type Boolean
6362      */
6363     hidden : false,
6364     /**
6365      * Read-only. True if this button is disabled
6366      * @type Boolean
6367      */
6368     disabled : false,
6369     /**
6370      * Read-only. True if this button is pressed (only if enableToggle = true)
6371      * @type Boolean
6372      */
6373     pressed : false,
6374
6375     /**
6376      * @cfg {Number} tabIndex 
6377      * The DOM tabIndex for this button (defaults to undefined)
6378      */
6379     tabIndex : undefined,
6380
6381     /**
6382      * @cfg {Boolean} enableToggle
6383      * True to enable pressed/not pressed toggling (defaults to false)
6384      */
6385     enableToggle: false,
6386     /**
6387      * @cfg {Roo.menu.Menu} menu
6388      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6389      */
6390     menu : undefined,
6391     /**
6392      * @cfg {String} menuAlign
6393      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6394      */
6395     menuAlign : "tl-bl?",
6396
6397     /**
6398      * @cfg {String} iconCls
6399      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6400      */
6401     iconCls : undefined,
6402     /**
6403      * @cfg {String} type
6404      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6405      */
6406     type : 'button',
6407
6408     // private
6409     menuClassTarget: 'tr',
6410
6411     /**
6412      * @cfg {String} clickEvent
6413      * The type of event to map to the button's event handler (defaults to 'click')
6414      */
6415     clickEvent : 'click',
6416
6417     /**
6418      * @cfg {Boolean} handleMouseEvents
6419      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6420      */
6421     handleMouseEvents : true,
6422
6423     /**
6424      * @cfg {String} tooltipType
6425      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6426      */
6427     tooltipType : 'qtip',
6428
6429     /**
6430      * @cfg {String} cls
6431      * A CSS class to apply to the button's main element.
6432      */
6433     
6434     /**
6435      * @cfg {Roo.Template} template (Optional)
6436      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6437      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6438      * require code modifications if required elements (e.g. a button) aren't present.
6439      */
6440
6441     // private
6442     render : function(renderTo){
6443         var btn;
6444         if(this.hideParent){
6445             this.parentEl = Roo.get(renderTo);
6446         }
6447         if(!this.dhconfig){
6448             if(!this.template){
6449                 if(!Roo.Button.buttonTemplate){
6450                     // hideous table template
6451                     Roo.Button.buttonTemplate = new Roo.Template(
6452                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6453                         '<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>',
6454                         "</tr></tbody></table>");
6455                 }
6456                 this.template = Roo.Button.buttonTemplate;
6457             }
6458             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6459             var btnEl = btn.child("button:first");
6460             btnEl.on('focus', this.onFocus, this);
6461             btnEl.on('blur', this.onBlur, this);
6462             if(this.cls){
6463                 btn.addClass(this.cls);
6464             }
6465             if(this.icon){
6466                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6467             }
6468             if(this.iconCls){
6469                 btnEl.addClass(this.iconCls);
6470                 if(!this.cls){
6471                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6472                 }
6473             }
6474             if(this.tabIndex !== undefined){
6475                 btnEl.dom.tabIndex = this.tabIndex;
6476             }
6477             if(this.tooltip){
6478                 if(typeof this.tooltip == 'object'){
6479                     Roo.QuickTips.tips(Roo.apply({
6480                           target: btnEl.id
6481                     }, this.tooltip));
6482                 } else {
6483                     btnEl.dom[this.tooltipType] = this.tooltip;
6484                 }
6485             }
6486         }else{
6487             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6488         }
6489         this.el = btn;
6490         if(this.id){
6491             this.el.dom.id = this.el.id = this.id;
6492         }
6493         if(this.menu){
6494             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6495             this.menu.on("show", this.onMenuShow, this);
6496             this.menu.on("hide", this.onMenuHide, this);
6497         }
6498         btn.addClass("x-btn");
6499         if(Roo.isIE && !Roo.isIE7){
6500             this.autoWidth.defer(1, this);
6501         }else{
6502             this.autoWidth();
6503         }
6504         if(this.handleMouseEvents){
6505             btn.on("mouseover", this.onMouseOver, this);
6506             btn.on("mouseout", this.onMouseOut, this);
6507             btn.on("mousedown", this.onMouseDown, this);
6508         }
6509         btn.on(this.clickEvent, this.onClick, this);
6510         //btn.on("mouseup", this.onMouseUp, this);
6511         if(this.hidden){
6512             this.hide();
6513         }
6514         if(this.disabled){
6515             this.disable();
6516         }
6517         Roo.ButtonToggleMgr.register(this);
6518         if(this.pressed){
6519             this.el.addClass("x-btn-pressed");
6520         }
6521         if(this.repeat){
6522             var repeater = new Roo.util.ClickRepeater(btn,
6523                 typeof this.repeat == "object" ? this.repeat : {}
6524             );
6525             repeater.on("click", this.onClick,  this);
6526         }
6527         
6528         this.fireEvent('render', this);
6529         
6530     },
6531     /**
6532      * Returns the button's underlying element
6533      * @return {Roo.Element} The element
6534      */
6535     getEl : function(){
6536         return this.el;  
6537     },
6538     
6539     /**
6540      * Destroys this Button and removes any listeners.
6541      */
6542     destroy : function(){
6543         Roo.ButtonToggleMgr.unregister(this);
6544         this.el.removeAllListeners();
6545         this.purgeListeners();
6546         this.el.remove();
6547     },
6548
6549     // private
6550     autoWidth : function(){
6551         if(this.el){
6552             this.el.setWidth("auto");
6553             if(Roo.isIE7 && Roo.isStrict){
6554                 var ib = this.el.child('button');
6555                 if(ib && ib.getWidth() > 20){
6556                     ib.clip();
6557                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6558                 }
6559             }
6560             if(this.minWidth){
6561                 if(this.hidden){
6562                     this.el.beginMeasure();
6563                 }
6564                 if(this.el.getWidth() < this.minWidth){
6565                     this.el.setWidth(this.minWidth);
6566                 }
6567                 if(this.hidden){
6568                     this.el.endMeasure();
6569                 }
6570             }
6571         }
6572     },
6573
6574     /**
6575      * Assigns this button's click handler
6576      * @param {Function} handler The function to call when the button is clicked
6577      * @param {Object} scope (optional) Scope for the function passed in
6578      */
6579     setHandler : function(handler, scope){
6580         this.handler = handler;
6581         this.scope = scope;  
6582     },
6583     
6584     /**
6585      * Sets this button's text
6586      * @param {String} text The button text
6587      */
6588     setText : function(text){
6589         this.text = text;
6590         if(this.el){
6591             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6592         }
6593         this.autoWidth();
6594     },
6595     
6596     /**
6597      * Gets the text for this button
6598      * @return {String} The button text
6599      */
6600     getText : function(){
6601         return this.text;  
6602     },
6603     
6604     /**
6605      * Show this button
6606      */
6607     show: function(){
6608         this.hidden = false;
6609         if(this.el){
6610             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6611         }
6612     },
6613     
6614     /**
6615      * Hide this button
6616      */
6617     hide: function(){
6618         this.hidden = true;
6619         if(this.el){
6620             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6621         }
6622     },
6623     
6624     /**
6625      * Convenience function for boolean show/hide
6626      * @param {Boolean} visible True to show, false to hide
6627      */
6628     setVisible: function(visible){
6629         if(visible) {
6630             this.show();
6631         }else{
6632             this.hide();
6633         }
6634     },
6635     
6636     /**
6637      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6638      * @param {Boolean} state (optional) Force a particular state
6639      */
6640     toggle : function(state){
6641         state = state === undefined ? !this.pressed : state;
6642         if(state != this.pressed){
6643             if(state){
6644                 this.el.addClass("x-btn-pressed");
6645                 this.pressed = true;
6646                 this.fireEvent("toggle", this, true);
6647             }else{
6648                 this.el.removeClass("x-btn-pressed");
6649                 this.pressed = false;
6650                 this.fireEvent("toggle", this, false);
6651             }
6652             if(this.toggleHandler){
6653                 this.toggleHandler.call(this.scope || this, this, state);
6654             }
6655         }
6656     },
6657     
6658     /**
6659      * Focus the button
6660      */
6661     focus : function(){
6662         this.el.child('button:first').focus();
6663     },
6664     
6665     /**
6666      * Disable this button
6667      */
6668     disable : function(){
6669         if(this.el){
6670             this.el.addClass("x-btn-disabled");
6671         }
6672         this.disabled = true;
6673     },
6674     
6675     /**
6676      * Enable this button
6677      */
6678     enable : function(){
6679         if(this.el){
6680             this.el.removeClass("x-btn-disabled");
6681         }
6682         this.disabled = false;
6683     },
6684
6685     /**
6686      * Convenience function for boolean enable/disable
6687      * @param {Boolean} enabled True to enable, false to disable
6688      */
6689     setDisabled : function(v){
6690         this[v !== true ? "enable" : "disable"]();
6691     },
6692
6693     // private
6694     onClick : function(e)
6695     {
6696         if(e){
6697             e.preventDefault();
6698         }
6699         if(e.button != 0){
6700             return;
6701         }
6702         if(!this.disabled){
6703             if(this.enableToggle){
6704                 this.toggle();
6705             }
6706             if(this.menu && !this.menu.isVisible()){
6707                 this.menu.show(this.el, this.menuAlign);
6708             }
6709             this.fireEvent("click", this, e);
6710             if(this.handler){
6711                 this.el.removeClass("x-btn-over");
6712                 this.handler.call(this.scope || this, this, e);
6713             }
6714         }
6715     },
6716     // private
6717     onMouseOver : function(e){
6718         if(!this.disabled){
6719             this.el.addClass("x-btn-over");
6720             this.fireEvent('mouseover', this, e);
6721         }
6722     },
6723     // private
6724     onMouseOut : function(e){
6725         if(!e.within(this.el,  true)){
6726             this.el.removeClass("x-btn-over");
6727             this.fireEvent('mouseout', this, e);
6728         }
6729     },
6730     // private
6731     onFocus : function(e){
6732         if(!this.disabled){
6733             this.el.addClass("x-btn-focus");
6734         }
6735     },
6736     // private
6737     onBlur : function(e){
6738         this.el.removeClass("x-btn-focus");
6739     },
6740     // private
6741     onMouseDown : function(e){
6742         if(!this.disabled && e.button == 0){
6743             this.el.addClass("x-btn-click");
6744             Roo.get(document).on('mouseup', this.onMouseUp, this);
6745         }
6746     },
6747     // private
6748     onMouseUp : function(e){
6749         if(e.button == 0){
6750             this.el.removeClass("x-btn-click");
6751             Roo.get(document).un('mouseup', this.onMouseUp, this);
6752         }
6753     },
6754     // private
6755     onMenuShow : function(e){
6756         this.el.addClass("x-btn-menu-active");
6757     },
6758     // private
6759     onMenuHide : function(e){
6760         this.el.removeClass("x-btn-menu-active");
6761     }   
6762 });
6763
6764 // Private utility class used by Button
6765 Roo.ButtonToggleMgr = function(){
6766    var groups = {};
6767    
6768    function toggleGroup(btn, state){
6769        if(state){
6770            var g = groups[btn.toggleGroup];
6771            for(var i = 0, l = g.length; i < l; i++){
6772                if(g[i] != btn){
6773                    g[i].toggle(false);
6774                }
6775            }
6776        }
6777    }
6778    
6779    return {
6780        register : function(btn){
6781            if(!btn.toggleGroup){
6782                return;
6783            }
6784            var g = groups[btn.toggleGroup];
6785            if(!g){
6786                g = groups[btn.toggleGroup] = [];
6787            }
6788            g.push(btn);
6789            btn.on("toggle", toggleGroup);
6790        },
6791        
6792        unregister : function(btn){
6793            if(!btn.toggleGroup){
6794                return;
6795            }
6796            var g = groups[btn.toggleGroup];
6797            if(g){
6798                g.remove(btn);
6799                btn.un("toggle", toggleGroup);
6800            }
6801        }
6802    };
6803 }();/*
6804  * Based on:
6805  * Ext JS Library 1.1.1
6806  * Copyright(c) 2006-2007, Ext JS, LLC.
6807  *
6808  * Originally Released Under LGPL - original licence link has changed is not relivant.
6809  *
6810  * Fork - LGPL
6811  * <script type="text/javascript">
6812  */
6813  
6814 /**
6815  * @class Roo.SplitButton
6816  * @extends Roo.Button
6817  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6818  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6819  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6820  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6821  * @cfg {String} arrowTooltip The title attribute of the arrow
6822  * @constructor
6823  * Create a new menu button
6824  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6825  * @param {Object} config The config object
6826  */
6827 Roo.SplitButton = function(renderTo, config){
6828     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6829     /**
6830      * @event arrowclick
6831      * Fires when this button's arrow is clicked
6832      * @param {SplitButton} this
6833      * @param {EventObject} e The click event
6834      */
6835     this.addEvents({"arrowclick":true});
6836 };
6837
6838 Roo.extend(Roo.SplitButton, Roo.Button, {
6839     render : function(renderTo){
6840         // this is one sweet looking template!
6841         var tpl = new Roo.Template(
6842             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6843             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6844             '<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>',
6845             "</tbody></table></td><td>",
6846             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6847             '<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>',
6848             "</tbody></table></td></tr></table>"
6849         );
6850         var btn = tpl.append(renderTo, [this.text, this.type], true);
6851         var btnEl = btn.child("button");
6852         if(this.cls){
6853             btn.addClass(this.cls);
6854         }
6855         if(this.icon){
6856             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6857         }
6858         if(this.iconCls){
6859             btnEl.addClass(this.iconCls);
6860             if(!this.cls){
6861                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6862             }
6863         }
6864         this.el = btn;
6865         if(this.handleMouseEvents){
6866             btn.on("mouseover", this.onMouseOver, this);
6867             btn.on("mouseout", this.onMouseOut, this);
6868             btn.on("mousedown", this.onMouseDown, this);
6869             btn.on("mouseup", this.onMouseUp, this);
6870         }
6871         btn.on(this.clickEvent, this.onClick, this);
6872         if(this.tooltip){
6873             if(typeof this.tooltip == 'object'){
6874                 Roo.QuickTips.tips(Roo.apply({
6875                       target: btnEl.id
6876                 }, this.tooltip));
6877             } else {
6878                 btnEl.dom[this.tooltipType] = this.tooltip;
6879             }
6880         }
6881         if(this.arrowTooltip){
6882             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6883         }
6884         if(this.hidden){
6885             this.hide();
6886         }
6887         if(this.disabled){
6888             this.disable();
6889         }
6890         if(this.pressed){
6891             this.el.addClass("x-btn-pressed");
6892         }
6893         if(Roo.isIE && !Roo.isIE7){
6894             this.autoWidth.defer(1, this);
6895         }else{
6896             this.autoWidth();
6897         }
6898         if(this.menu){
6899             this.menu.on("show", this.onMenuShow, this);
6900             this.menu.on("hide", this.onMenuHide, this);
6901         }
6902         this.fireEvent('render', this);
6903     },
6904
6905     // private
6906     autoWidth : function(){
6907         if(this.el){
6908             var tbl = this.el.child("table:first");
6909             var tbl2 = this.el.child("table:last");
6910             this.el.setWidth("auto");
6911             tbl.setWidth("auto");
6912             if(Roo.isIE7 && Roo.isStrict){
6913                 var ib = this.el.child('button:first');
6914                 if(ib && ib.getWidth() > 20){
6915                     ib.clip();
6916                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6917                 }
6918             }
6919             if(this.minWidth){
6920                 if(this.hidden){
6921                     this.el.beginMeasure();
6922                 }
6923                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6924                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6925                 }
6926                 if(this.hidden){
6927                     this.el.endMeasure();
6928                 }
6929             }
6930             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6931         } 
6932     },
6933     /**
6934      * Sets this button's click handler
6935      * @param {Function} handler The function to call when the button is clicked
6936      * @param {Object} scope (optional) Scope for the function passed above
6937      */
6938     setHandler : function(handler, scope){
6939         this.handler = handler;
6940         this.scope = scope;  
6941     },
6942     
6943     /**
6944      * Sets this button's arrow click handler
6945      * @param {Function} handler The function to call when the arrow is clicked
6946      * @param {Object} scope (optional) Scope for the function passed above
6947      */
6948     setArrowHandler : function(handler, scope){
6949         this.arrowHandler = handler;
6950         this.scope = scope;  
6951     },
6952     
6953     /**
6954      * Focus the button
6955      */
6956     focus : function(){
6957         if(this.el){
6958             this.el.child("button:first").focus();
6959         }
6960     },
6961
6962     // private
6963     onClick : function(e){
6964         e.preventDefault();
6965         if(!this.disabled){
6966             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6967                 if(this.menu && !this.menu.isVisible()){
6968                     this.menu.show(this.el, this.menuAlign);
6969                 }
6970                 this.fireEvent("arrowclick", this, e);
6971                 if(this.arrowHandler){
6972                     this.arrowHandler.call(this.scope || this, this, e);
6973                 }
6974             }else{
6975                 this.fireEvent("click", this, e);
6976                 if(this.handler){
6977                     this.handler.call(this.scope || this, this, e);
6978                 }
6979             }
6980         }
6981     },
6982     // private
6983     onMouseDown : function(e){
6984         if(!this.disabled){
6985             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6986         }
6987     },
6988     // private
6989     onMouseUp : function(e){
6990         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6991     }   
6992 });
6993
6994
6995 // backwards compat
6996 Roo.MenuButton = Roo.SplitButton;/*
6997  * Based on:
6998  * Ext JS Library 1.1.1
6999  * Copyright(c) 2006-2007, Ext JS, LLC.
7000  *
7001  * Originally Released Under LGPL - original licence link has changed is not relivant.
7002  *
7003  * Fork - LGPL
7004  * <script type="text/javascript">
7005  */
7006
7007 /**
7008  * @class Roo.Toolbar
7009  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
7010  * Basic Toolbar class.
7011  * @constructor
7012  * Creates a new Toolbar
7013  * @param {Object} container The config object
7014  */ 
7015 Roo.Toolbar = function(container, buttons, config)
7016 {
7017     /// old consturctor format still supported..
7018     if(container instanceof Array){ // omit the container for later rendering
7019         buttons = container;
7020         config = buttons;
7021         container = null;
7022     }
7023     if (typeof(container) == 'object' && container.xtype) {
7024         config = container;
7025         container = config.container;
7026         buttons = config.buttons || []; // not really - use items!!
7027     }
7028     var xitems = [];
7029     if (config && config.items) {
7030         xitems = config.items;
7031         delete config.items;
7032     }
7033     Roo.apply(this, config);
7034     this.buttons = buttons;
7035     
7036     if(container){
7037         this.render(container);
7038     }
7039     this.xitems = xitems;
7040     Roo.each(xitems, function(b) {
7041         this.add(b);
7042     }, this);
7043     
7044 };
7045
7046 Roo.Toolbar.prototype = {
7047     /**
7048      * @cfg {Array} items
7049      * array of button configs or elements to add (will be converted to a MixedCollection)
7050      */
7051     items: false,
7052     /**
7053      * @cfg {String/HTMLElement/Element} container
7054      * The id or element that will contain the toolbar
7055      */
7056     // private
7057     render : function(ct){
7058         this.el = Roo.get(ct);
7059         if(this.cls){
7060             this.el.addClass(this.cls);
7061         }
7062         // using a table allows for vertical alignment
7063         // 100% width is needed by Safari...
7064         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7065         this.tr = this.el.child("tr", true);
7066         var autoId = 0;
7067         this.items = new Roo.util.MixedCollection(false, function(o){
7068             return o.id || ("item" + (++autoId));
7069         });
7070         if(this.buttons){
7071             this.add.apply(this, this.buttons);
7072             delete this.buttons;
7073         }
7074     },
7075
7076     /**
7077      * Adds element(s) to the toolbar -- this function takes a variable number of 
7078      * arguments of mixed type and adds them to the toolbar.
7079      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7080      * <ul>
7081      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7082      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7083      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7084      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7085      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7086      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7087      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7088      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7089      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7090      * </ul>
7091      * @param {Mixed} arg2
7092      * @param {Mixed} etc.
7093      */
7094     add : function(){
7095         var a = arguments, l = a.length;
7096         for(var i = 0; i < l; i++){
7097             this._add(a[i]);
7098         }
7099     },
7100     // private..
7101     _add : function(el) {
7102         
7103         if (el.xtype) {
7104             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7105         }
7106         
7107         if (el.applyTo){ // some kind of form field
7108             return this.addField(el);
7109         } 
7110         if (el.render){ // some kind of Toolbar.Item
7111             return this.addItem(el);
7112         }
7113         if (typeof el == "string"){ // string
7114             if(el == "separator" || el == "-"){
7115                 return this.addSeparator();
7116             }
7117             if (el == " "){
7118                 return this.addSpacer();
7119             }
7120             if(el == "->"){
7121                 return this.addFill();
7122             }
7123             return this.addText(el);
7124             
7125         }
7126         if(el.tagName){ // element
7127             return this.addElement(el);
7128         }
7129         if(typeof el == "object"){ // must be button config?
7130             return this.addButton(el);
7131         }
7132         // and now what?!?!
7133         return false;
7134         
7135     },
7136     
7137     /**
7138      * Add an Xtype element
7139      * @param {Object} xtype Xtype Object
7140      * @return {Object} created Object
7141      */
7142     addxtype : function(e){
7143         return this.add(e);  
7144     },
7145     
7146     /**
7147      * Returns the Element for this toolbar.
7148      * @return {Roo.Element}
7149      */
7150     getEl : function(){
7151         return this.el;  
7152     },
7153     
7154     /**
7155      * Adds a separator
7156      * @return {Roo.Toolbar.Item} The separator item
7157      */
7158     addSeparator : function(){
7159         return this.addItem(new Roo.Toolbar.Separator());
7160     },
7161
7162     /**
7163      * Adds a spacer element
7164      * @return {Roo.Toolbar.Spacer} The spacer item
7165      */
7166     addSpacer : function(){
7167         return this.addItem(new Roo.Toolbar.Spacer());
7168     },
7169
7170     /**
7171      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7172      * @return {Roo.Toolbar.Fill} The fill item
7173      */
7174     addFill : function(){
7175         return this.addItem(new Roo.Toolbar.Fill());
7176     },
7177
7178     /**
7179      * Adds any standard HTML element to the toolbar
7180      * @param {String/HTMLElement/Element} el The element or id of the element to add
7181      * @return {Roo.Toolbar.Item} The element's item
7182      */
7183     addElement : function(el){
7184         return this.addItem(new Roo.Toolbar.Item(el));
7185     },
7186     /**
7187      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7188      * @type Roo.util.MixedCollection  
7189      */
7190     items : false,
7191      
7192     /**
7193      * Adds any Toolbar.Item or subclass
7194      * @param {Roo.Toolbar.Item} item
7195      * @return {Roo.Toolbar.Item} The item
7196      */
7197     addItem : function(item){
7198         var td = this.nextBlock();
7199         item.render(td);
7200         this.items.add(item);
7201         return item;
7202     },
7203     
7204     /**
7205      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7206      * @param {Object/Array} config A button config or array of configs
7207      * @return {Roo.Toolbar.Button/Array}
7208      */
7209     addButton : function(config){
7210         if(config instanceof Array){
7211             var buttons = [];
7212             for(var i = 0, len = config.length; i < len; i++) {
7213                 buttons.push(this.addButton(config[i]));
7214             }
7215             return buttons;
7216         }
7217         var b = config;
7218         if(!(config instanceof Roo.Toolbar.Button)){
7219             b = config.split ?
7220                 new Roo.Toolbar.SplitButton(config) :
7221                 new Roo.Toolbar.Button(config);
7222         }
7223         var td = this.nextBlock();
7224         b.render(td);
7225         this.items.add(b);
7226         return b;
7227     },
7228     
7229     /**
7230      * Adds text to the toolbar
7231      * @param {String} text The text to add
7232      * @return {Roo.Toolbar.Item} The element's item
7233      */
7234     addText : function(text){
7235         return this.addItem(new Roo.Toolbar.TextItem(text));
7236     },
7237     
7238     /**
7239      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7240      * @param {Number} index The index where the item is to be inserted
7241      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7242      * @return {Roo.Toolbar.Button/Item}
7243      */
7244     insertButton : function(index, item){
7245         if(item instanceof Array){
7246             var buttons = [];
7247             for(var i = 0, len = item.length; i < len; i++) {
7248                buttons.push(this.insertButton(index + i, item[i]));
7249             }
7250             return buttons;
7251         }
7252         if (!(item instanceof Roo.Toolbar.Button)){
7253            item = new Roo.Toolbar.Button(item);
7254         }
7255         var td = document.createElement("td");
7256         this.tr.insertBefore(td, this.tr.childNodes[index]);
7257         item.render(td);
7258         this.items.insert(index, item);
7259         return item;
7260     },
7261     
7262     /**
7263      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7264      * @param {Object} config
7265      * @return {Roo.Toolbar.Item} The element's item
7266      */
7267     addDom : function(config, returnEl){
7268         var td = this.nextBlock();
7269         Roo.DomHelper.overwrite(td, config);
7270         var ti = new Roo.Toolbar.Item(td.firstChild);
7271         ti.render(td);
7272         this.items.add(ti);
7273         return ti;
7274     },
7275
7276     /**
7277      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7278      * @type Roo.util.MixedCollection  
7279      */
7280     fields : false,
7281     
7282     /**
7283      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7284      * Note: the field should not have been rendered yet. For a field that has already been
7285      * rendered, use {@link #addElement}.
7286      * @param {Roo.form.Field} field
7287      * @return {Roo.ToolbarItem}
7288      */
7289      
7290       
7291     addField : function(field) {
7292         if (!this.fields) {
7293             var autoId = 0;
7294             this.fields = new Roo.util.MixedCollection(false, function(o){
7295                 return o.id || ("item" + (++autoId));
7296             });
7297
7298         }
7299         
7300         var td = this.nextBlock();
7301         field.render(td);
7302         var ti = new Roo.Toolbar.Item(td.firstChild);
7303         ti.render(td);
7304         this.items.add(ti);
7305         this.fields.add(field);
7306         return ti;
7307     },
7308     /**
7309      * Hide the toolbar
7310      * @method hide
7311      */
7312      
7313       
7314     hide : function()
7315     {
7316         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7317         this.el.child('div').hide();
7318     },
7319     /**
7320      * Show the toolbar
7321      * @method show
7322      */
7323     show : function()
7324     {
7325         this.el.child('div').show();
7326     },
7327       
7328     // private
7329     nextBlock : function(){
7330         var td = document.createElement("td");
7331         this.tr.appendChild(td);
7332         return td;
7333     },
7334
7335     // private
7336     destroy : function(){
7337         if(this.items){ // rendered?
7338             Roo.destroy.apply(Roo, this.items.items);
7339         }
7340         if(this.fields){ // rendered?
7341             Roo.destroy.apply(Roo, this.fields.items);
7342         }
7343         Roo.Element.uncache(this.el, this.tr);
7344     }
7345 };
7346
7347 /**
7348  * @class Roo.Toolbar.Item
7349  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7350  * @constructor
7351  * Creates a new Item
7352  * @param {HTMLElement} el 
7353  */
7354 Roo.Toolbar.Item = function(el){
7355     var cfg = {};
7356     if (typeof (el.xtype) != 'undefined') {
7357         cfg = el;
7358         el = cfg.el;
7359     }
7360     
7361     this.el = Roo.getDom(el);
7362     this.id = Roo.id(this.el);
7363     this.hidden = false;
7364     
7365     this.addEvents({
7366          /**
7367              * @event render
7368              * Fires when the button is rendered
7369              * @param {Button} this
7370              */
7371         'render': true
7372     });
7373     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7374 };
7375 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7376 //Roo.Toolbar.Item.prototype = {
7377     
7378     /**
7379      * Get this item's HTML Element
7380      * @return {HTMLElement}
7381      */
7382     getEl : function(){
7383        return this.el;  
7384     },
7385
7386     // private
7387     render : function(td){
7388         
7389          this.td = td;
7390         td.appendChild(this.el);
7391         
7392         this.fireEvent('render', this);
7393     },
7394     
7395     /**
7396      * Removes and destroys this item.
7397      */
7398     destroy : function(){
7399         this.td.parentNode.removeChild(this.td);
7400     },
7401     
7402     /**
7403      * Shows this item.
7404      */
7405     show: function(){
7406         this.hidden = false;
7407         this.td.style.display = "";
7408     },
7409     
7410     /**
7411      * Hides this item.
7412      */
7413     hide: function(){
7414         this.hidden = true;
7415         this.td.style.display = "none";
7416     },
7417     
7418     /**
7419      * Convenience function for boolean show/hide.
7420      * @param {Boolean} visible true to show/false to hide
7421      */
7422     setVisible: function(visible){
7423         if(visible) {
7424             this.show();
7425         }else{
7426             this.hide();
7427         }
7428     },
7429     
7430     /**
7431      * Try to focus this item.
7432      */
7433     focus : function(){
7434         Roo.fly(this.el).focus();
7435     },
7436     
7437     /**
7438      * Disables this item.
7439      */
7440     disable : function(){
7441         Roo.fly(this.td).addClass("x-item-disabled");
7442         this.disabled = true;
7443         this.el.disabled = true;
7444     },
7445     
7446     /**
7447      * Enables this item.
7448      */
7449     enable : function(){
7450         Roo.fly(this.td).removeClass("x-item-disabled");
7451         this.disabled = false;
7452         this.el.disabled = false;
7453     }
7454 });
7455
7456
7457 /**
7458  * @class Roo.Toolbar.Separator
7459  * @extends Roo.Toolbar.Item
7460  * A simple toolbar separator class
7461  * @constructor
7462  * Creates a new Separator
7463  */
7464 Roo.Toolbar.Separator = function(cfg){
7465     
7466     var s = document.createElement("span");
7467     s.className = "ytb-sep";
7468     if (cfg) {
7469         cfg.el = s;
7470     }
7471     
7472     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7473 };
7474 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7475     enable:Roo.emptyFn,
7476     disable:Roo.emptyFn,
7477     focus:Roo.emptyFn
7478 });
7479
7480 /**
7481  * @class Roo.Toolbar.Spacer
7482  * @extends Roo.Toolbar.Item
7483  * A simple element that adds extra horizontal space to a toolbar.
7484  * @constructor
7485  * Creates a new Spacer
7486  */
7487 Roo.Toolbar.Spacer = function(cfg){
7488     var s = document.createElement("div");
7489     s.className = "ytb-spacer";
7490     if (cfg) {
7491         cfg.el = s;
7492     }
7493     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7494 };
7495 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7496     enable:Roo.emptyFn,
7497     disable:Roo.emptyFn,
7498     focus:Roo.emptyFn
7499 });
7500
7501 /**
7502  * @class Roo.Toolbar.Fill
7503  * @extends Roo.Toolbar.Spacer
7504  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7505  * @constructor
7506  * Creates a new Spacer
7507  */
7508 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7509     // private
7510     render : function(td){
7511         td.style.width = '100%';
7512         Roo.Toolbar.Fill.superclass.render.call(this, td);
7513     }
7514 });
7515
7516 /**
7517  * @class Roo.Toolbar.TextItem
7518  * @extends Roo.Toolbar.Item
7519  * A simple class that renders text directly into a toolbar.
7520  * @constructor
7521  * Creates a new TextItem
7522  * @cfg {string} text 
7523  */
7524 Roo.Toolbar.TextItem = function(cfg){
7525     var  text = cfg || "";
7526     if (typeof(cfg) == 'object') {
7527         text = cfg.text || "";
7528     }  else {
7529         cfg = null;
7530     }
7531     var s = document.createElement("span");
7532     s.className = "ytb-text";
7533     s.innerHTML = text;
7534     if (cfg) {
7535         cfg.el  = s;
7536     }
7537     
7538     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7539 };
7540 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7541     
7542      
7543     enable:Roo.emptyFn,
7544     disable:Roo.emptyFn,
7545     focus:Roo.emptyFn,
7546      /**
7547      * Shows this button
7548      */
7549     show: function(){
7550         this.hidden = false;
7551         this.el.style.display = "";
7552     },
7553     
7554     /**
7555      * Hides this button
7556      */
7557     hide: function(){
7558         this.hidden = true;
7559         this.el.style.display = "none";
7560     }
7561     
7562 });
7563
7564 /**
7565  * @class Roo.Toolbar.Button
7566  * @extends Roo.Button
7567  * A button that renders into a toolbar.
7568  * @constructor
7569  * Creates a new Button
7570  * @param {Object} config A standard {@link Roo.Button} config object
7571  */
7572 Roo.Toolbar.Button = function(config){
7573     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7574 };
7575 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7576 {
7577     
7578     
7579     render : function(td){
7580         this.td = td;
7581         Roo.Toolbar.Button.superclass.render.call(this, td);
7582     },
7583     
7584     /**
7585      * Removes and destroys this button
7586      */
7587     destroy : function(){
7588         Roo.Toolbar.Button.superclass.destroy.call(this);
7589         this.td.parentNode.removeChild(this.td);
7590     },
7591     
7592     /**
7593      * Shows this button
7594      */
7595     show: function(){
7596         this.hidden = false;
7597         this.td.style.display = "";
7598     },
7599     
7600     /**
7601      * Hides this button
7602      */
7603     hide: function(){
7604         this.hidden = true;
7605         this.td.style.display = "none";
7606     },
7607
7608     /**
7609      * Disables this item
7610      */
7611     disable : function(){
7612         Roo.fly(this.td).addClass("x-item-disabled");
7613         this.disabled = true;
7614     },
7615
7616     /**
7617      * Enables this item
7618      */
7619     enable : function(){
7620         Roo.fly(this.td).removeClass("x-item-disabled");
7621         this.disabled = false;
7622     }
7623 });
7624 // backwards compat
7625 Roo.ToolbarButton = Roo.Toolbar.Button;
7626
7627 /**
7628  * @class Roo.Toolbar.SplitButton
7629  * @extends Roo.SplitButton
7630  * A menu button that renders into a toolbar.
7631  * @constructor
7632  * Creates a new SplitButton
7633  * @param {Object} config A standard {@link Roo.SplitButton} config object
7634  */
7635 Roo.Toolbar.SplitButton = function(config){
7636     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7637 };
7638 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7639     render : function(td){
7640         this.td = td;
7641         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7642     },
7643     
7644     /**
7645      * Removes and destroys this button
7646      */
7647     destroy : function(){
7648         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7649         this.td.parentNode.removeChild(this.td);
7650     },
7651     
7652     /**
7653      * Shows this button
7654      */
7655     show: function(){
7656         this.hidden = false;
7657         this.td.style.display = "";
7658     },
7659     
7660     /**
7661      * Hides this button
7662      */
7663     hide: function(){
7664         this.hidden = true;
7665         this.td.style.display = "none";
7666     }
7667 });
7668
7669 // backwards compat
7670 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7671  * Based on:
7672  * Ext JS Library 1.1.1
7673  * Copyright(c) 2006-2007, Ext JS, LLC.
7674  *
7675  * Originally Released Under LGPL - original licence link has changed is not relivant.
7676  *
7677  * Fork - LGPL
7678  * <script type="text/javascript">
7679  */
7680  
7681 /**
7682  * @class Roo.PagingToolbar
7683  * @extends Roo.Toolbar
7684  * @children   Roo.Toolbar.Item Roo.form.Field
7685  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7686  * @constructor
7687  * Create a new PagingToolbar
7688  * @param {Object} config The config object
7689  */
7690 Roo.PagingToolbar = function(el, ds, config)
7691 {
7692     // old args format still supported... - xtype is prefered..
7693     if (typeof(el) == 'object' && el.xtype) {
7694         // created from xtype...
7695         config = el;
7696         ds = el.dataSource;
7697         el = config.container;
7698     }
7699     var items = [];
7700     if (config.items) {
7701         items = config.items;
7702         config.items = [];
7703     }
7704     
7705     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7706     this.ds = ds;
7707     this.cursor = 0;
7708     this.renderButtons(this.el);
7709     this.bind(ds);
7710     
7711     // supprot items array.
7712    
7713     Roo.each(items, function(e) {
7714         this.add(Roo.factory(e));
7715     },this);
7716     
7717 };
7718
7719 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7720    
7721     /**
7722      * @cfg {String/HTMLElement/Element} container
7723      * container The id or element that will contain the toolbar
7724      */
7725     /**
7726      * @cfg {Boolean} displayInfo
7727      * True to display the displayMsg (defaults to false)
7728      */
7729     
7730     
7731     /**
7732      * @cfg {Number} pageSize
7733      * The number of records to display per page (defaults to 20)
7734      */
7735     pageSize: 20,
7736     /**
7737      * @cfg {String} displayMsg
7738      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7739      */
7740     displayMsg : 'Displaying {0} - {1} of {2}',
7741     /**
7742      * @cfg {String} emptyMsg
7743      * The message to display when no records are found (defaults to "No data to display")
7744      */
7745     emptyMsg : 'No data to display',
7746     /**
7747      * Customizable piece of the default paging text (defaults to "Page")
7748      * @type String
7749      */
7750     beforePageText : "Page",
7751     /**
7752      * Customizable piece of the default paging text (defaults to "of %0")
7753      * @type String
7754      */
7755     afterPageText : "of {0}",
7756     /**
7757      * Customizable piece of the default paging text (defaults to "First Page")
7758      * @type String
7759      */
7760     firstText : "First Page",
7761     /**
7762      * Customizable piece of the default paging text (defaults to "Previous Page")
7763      * @type String
7764      */
7765     prevText : "Previous Page",
7766     /**
7767      * Customizable piece of the default paging text (defaults to "Next Page")
7768      * @type String
7769      */
7770     nextText : "Next Page",
7771     /**
7772      * Customizable piece of the default paging text (defaults to "Last Page")
7773      * @type String
7774      */
7775     lastText : "Last Page",
7776     /**
7777      * Customizable piece of the default paging text (defaults to "Refresh")
7778      * @type String
7779      */
7780     refreshText : "Refresh",
7781
7782     // private
7783     renderButtons : function(el){
7784         Roo.PagingToolbar.superclass.render.call(this, el);
7785         this.first = this.addButton({
7786             tooltip: this.firstText,
7787             cls: "x-btn-icon x-grid-page-first",
7788             disabled: true,
7789             handler: this.onClick.createDelegate(this, ["first"])
7790         });
7791         this.prev = this.addButton({
7792             tooltip: this.prevText,
7793             cls: "x-btn-icon x-grid-page-prev",
7794             disabled: true,
7795             handler: this.onClick.createDelegate(this, ["prev"])
7796         });
7797         //this.addSeparator();
7798         this.add(this.beforePageText);
7799         this.field = Roo.get(this.addDom({
7800            tag: "input",
7801            type: "text",
7802            size: "3",
7803            value: "1",
7804            cls: "x-grid-page-number"
7805         }).el);
7806         this.field.on("keydown", this.onPagingKeydown, this);
7807         this.field.on("focus", function(){this.dom.select();});
7808         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7809         this.field.setHeight(18);
7810         //this.addSeparator();
7811         this.next = this.addButton({
7812             tooltip: this.nextText,
7813             cls: "x-btn-icon x-grid-page-next",
7814             disabled: true,
7815             handler: this.onClick.createDelegate(this, ["next"])
7816         });
7817         this.last = this.addButton({
7818             tooltip: this.lastText,
7819             cls: "x-btn-icon x-grid-page-last",
7820             disabled: true,
7821             handler: this.onClick.createDelegate(this, ["last"])
7822         });
7823         //this.addSeparator();
7824         this.loading = this.addButton({
7825             tooltip: this.refreshText,
7826             cls: "x-btn-icon x-grid-loading",
7827             handler: this.onClick.createDelegate(this, ["refresh"])
7828         });
7829
7830         if(this.displayInfo){
7831             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7832         }
7833     },
7834
7835     // private
7836     updateInfo : function(){
7837         if(this.displayEl){
7838             var count = this.ds.getCount();
7839             var msg = count == 0 ?
7840                 this.emptyMsg :
7841                 String.format(
7842                     this.displayMsg,
7843                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7844                 );
7845             this.displayEl.update(msg);
7846         }
7847     },
7848
7849     // private
7850     onLoad : function(ds, r, o){
7851        this.cursor = o.params ? o.params.start : 0;
7852        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7853
7854        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7855        this.field.dom.value = ap;
7856        this.first.setDisabled(ap == 1);
7857        this.prev.setDisabled(ap == 1);
7858        this.next.setDisabled(ap == ps);
7859        this.last.setDisabled(ap == ps);
7860        this.loading.enable();
7861        this.updateInfo();
7862     },
7863
7864     // private
7865     getPageData : function(){
7866         var total = this.ds.getTotalCount();
7867         return {
7868             total : total,
7869             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7870             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7871         };
7872     },
7873
7874     // private
7875     onLoadError : function(){
7876         this.loading.enable();
7877     },
7878
7879     // private
7880     onPagingKeydown : function(e){
7881         var k = e.getKey();
7882         var d = this.getPageData();
7883         if(k == e.RETURN){
7884             var v = this.field.dom.value, pageNum;
7885             if(!v || isNaN(pageNum = parseInt(v, 10))){
7886                 this.field.dom.value = d.activePage;
7887                 return;
7888             }
7889             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7890             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7891             e.stopEvent();
7892         }
7893         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))
7894         {
7895           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7896           this.field.dom.value = pageNum;
7897           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7898           e.stopEvent();
7899         }
7900         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7901         {
7902           var v = this.field.dom.value, pageNum; 
7903           var increment = (e.shiftKey) ? 10 : 1;
7904           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7905             increment *= -1;
7906           }
7907           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7908             this.field.dom.value = d.activePage;
7909             return;
7910           }
7911           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7912           {
7913             this.field.dom.value = parseInt(v, 10) + increment;
7914             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7915             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7916           }
7917           e.stopEvent();
7918         }
7919     },
7920
7921     // private
7922     beforeLoad : function(){
7923         if(this.loading){
7924             this.loading.disable();
7925         }
7926     },
7927
7928     // private
7929     onClick : function(which){
7930         var ds = this.ds;
7931         switch(which){
7932             case "first":
7933                 ds.load({params:{start: 0, limit: this.pageSize}});
7934             break;
7935             case "prev":
7936                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7937             break;
7938             case "next":
7939                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7940             break;
7941             case "last":
7942                 var total = ds.getTotalCount();
7943                 var extra = total % this.pageSize;
7944                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7945                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7946             break;
7947             case "refresh":
7948                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7949             break;
7950         }
7951     },
7952
7953     /**
7954      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7955      * @param {Roo.data.Store} store The data store to unbind
7956      */
7957     unbind : function(ds){
7958         ds.un("beforeload", this.beforeLoad, this);
7959         ds.un("load", this.onLoad, this);
7960         ds.un("loadexception", this.onLoadError, this);
7961         ds.un("remove", this.updateInfo, this);
7962         ds.un("add", this.updateInfo, this);
7963         this.ds = undefined;
7964     },
7965
7966     /**
7967      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7968      * @param {Roo.data.Store} store The data store to bind
7969      */
7970     bind : function(ds){
7971         ds.on("beforeload", this.beforeLoad, this);
7972         ds.on("load", this.onLoad, this);
7973         ds.on("loadexception", this.onLoadError, this);
7974         ds.on("remove", this.updateInfo, this);
7975         ds.on("add", this.updateInfo, this);
7976         this.ds = ds;
7977     }
7978 });/*
7979  * Based on:
7980  * Ext JS Library 1.1.1
7981  * Copyright(c) 2006-2007, Ext JS, LLC.
7982  *
7983  * Originally Released Under LGPL - original licence link has changed is not relivant.
7984  *
7985  * Fork - LGPL
7986  * <script type="text/javascript">
7987  */
7988
7989 /**
7990  * @class Roo.Resizable
7991  * @extends Roo.util.Observable
7992  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7993  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7994  * 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
7995  * the element will be wrapped for you automatically.</p>
7996  * <p>Here is the list of valid resize handles:</p>
7997  * <pre>
7998 Value   Description
7999 ------  -------------------
8000  'n'     north
8001  's'     south
8002  'e'     east
8003  'w'     west
8004  'nw'    northwest
8005  'sw'    southwest
8006  'se'    southeast
8007  'ne'    northeast
8008  'hd'    horizontal drag
8009  'all'   all
8010 </pre>
8011  * <p>Here's an example showing the creation of a typical Resizable:</p>
8012  * <pre><code>
8013 var resizer = new Roo.Resizable("element-id", {
8014     handles: 'all',
8015     minWidth: 200,
8016     minHeight: 100,
8017     maxWidth: 500,
8018     maxHeight: 400,
8019     pinned: true
8020 });
8021 resizer.on("resize", myHandler);
8022 </code></pre>
8023  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8024  * resizer.east.setDisplayed(false);</p>
8025  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8026  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8027  * resize operation's new size (defaults to [0, 0])
8028  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8029  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8030  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8031  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8032  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8033  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8034  * @cfg {Number} width The width of the element in pixels (defaults to null)
8035  * @cfg {Number} height The height of the element in pixels (defaults to null)
8036  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8037  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8038  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8039  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8040  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8041  * in favor of the handles config option (defaults to false)
8042  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8043  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8044  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8045  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8046  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8047  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8048  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8049  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8050  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8051  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8052  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8053  * @constructor
8054  * Create a new resizable component
8055  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8056  * @param {Object} config configuration options
8057   */
8058 Roo.Resizable = function(el, config)
8059 {
8060     this.el = Roo.get(el);
8061
8062     if(config && config.wrap){
8063         config.resizeChild = this.el;
8064         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8065         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8066         this.el.setStyle("overflow", "hidden");
8067         this.el.setPositioning(config.resizeChild.getPositioning());
8068         config.resizeChild.clearPositioning();
8069         if(!config.width || !config.height){
8070             var csize = config.resizeChild.getSize();
8071             this.el.setSize(csize.width, csize.height);
8072         }
8073         if(config.pinned && !config.adjustments){
8074             config.adjustments = "auto";
8075         }
8076     }
8077
8078     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8079     this.proxy.unselectable();
8080     this.proxy.enableDisplayMode('block');
8081
8082     Roo.apply(this, config);
8083
8084     if(this.pinned){
8085         this.disableTrackOver = true;
8086         this.el.addClass("x-resizable-pinned");
8087     }
8088     // if the element isn't positioned, make it relative
8089     var position = this.el.getStyle("position");
8090     if(position != "absolute" && position != "fixed"){
8091         this.el.setStyle("position", "relative");
8092     }
8093     if(!this.handles){ // no handles passed, must be legacy style
8094         this.handles = 's,e,se';
8095         if(this.multiDirectional){
8096             this.handles += ',n,w';
8097         }
8098     }
8099     if(this.handles == "all"){
8100         this.handles = "n s e w ne nw se sw";
8101     }
8102     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8103     var ps = Roo.Resizable.positions;
8104     for(var i = 0, len = hs.length; i < len; i++){
8105         if(hs[i] && ps[hs[i]]){
8106             var pos = ps[hs[i]];
8107             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8108         }
8109     }
8110     // legacy
8111     this.corner = this.southeast;
8112     
8113     // updateBox = the box can move..
8114     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8115         this.updateBox = true;
8116     }
8117
8118     this.activeHandle = null;
8119
8120     if(this.resizeChild){
8121         if(typeof this.resizeChild == "boolean"){
8122             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8123         }else{
8124             this.resizeChild = Roo.get(this.resizeChild, true);
8125         }
8126     }
8127     
8128     if(this.adjustments == "auto"){
8129         var rc = this.resizeChild;
8130         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8131         if(rc && (hw || hn)){
8132             rc.position("relative");
8133             rc.setLeft(hw ? hw.el.getWidth() : 0);
8134             rc.setTop(hn ? hn.el.getHeight() : 0);
8135         }
8136         this.adjustments = [
8137             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8138             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8139         ];
8140     }
8141
8142     if(this.draggable){
8143         this.dd = this.dynamic ?
8144             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8145         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8146     }
8147
8148     // public events
8149     this.addEvents({
8150         /**
8151          * @event beforeresize
8152          * Fired before resize is allowed. Set enabled to false to cancel resize.
8153          * @param {Roo.Resizable} this
8154          * @param {Roo.EventObject} e The mousedown event
8155          */
8156         "beforeresize" : true,
8157         /**
8158          * @event resizing
8159          * Fired a resizing.
8160          * @param {Roo.Resizable} this
8161          * @param {Number} x The new x position
8162          * @param {Number} y The new y position
8163          * @param {Number} w The new w width
8164          * @param {Number} h The new h hight
8165          * @param {Roo.EventObject} e The mouseup event
8166          */
8167         "resizing" : true,
8168         /**
8169          * @event resize
8170          * Fired after a resize.
8171          * @param {Roo.Resizable} this
8172          * @param {Number} width The new width
8173          * @param {Number} height The new height
8174          * @param {Roo.EventObject} e The mouseup event
8175          */
8176         "resize" : true
8177     });
8178
8179     if(this.width !== null && this.height !== null){
8180         this.resizeTo(this.width, this.height);
8181     }else{
8182         this.updateChildSize();
8183     }
8184     if(Roo.isIE){
8185         this.el.dom.style.zoom = 1;
8186     }
8187     Roo.Resizable.superclass.constructor.call(this);
8188 };
8189
8190 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8191         resizeChild : false,
8192         adjustments : [0, 0],
8193         minWidth : 5,
8194         minHeight : 5,
8195         maxWidth : 10000,
8196         maxHeight : 10000,
8197         enabled : true,
8198         animate : false,
8199         duration : .35,
8200         dynamic : false,
8201         handles : false,
8202         multiDirectional : false,
8203         disableTrackOver : false,
8204         easing : 'easeOutStrong',
8205         widthIncrement : 0,
8206         heightIncrement : 0,
8207         pinned : false,
8208         width : null,
8209         height : null,
8210         preserveRatio : false,
8211         transparent: false,
8212         minX: 0,
8213         minY: 0,
8214         draggable: false,
8215
8216         /**
8217          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8218          */
8219         constrainTo: undefined,
8220         /**
8221          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8222          */
8223         resizeRegion: undefined,
8224
8225
8226     /**
8227      * Perform a manual resize
8228      * @param {Number} width
8229      * @param {Number} height
8230      */
8231     resizeTo : function(width, height){
8232         this.el.setSize(width, height);
8233         this.updateChildSize();
8234         this.fireEvent("resize", this, width, height, null);
8235     },
8236
8237     // private
8238     startSizing : function(e, handle){
8239         this.fireEvent("beforeresize", this, e);
8240         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8241
8242             if(!this.overlay){
8243                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8244                 this.overlay.unselectable();
8245                 this.overlay.enableDisplayMode("block");
8246                 this.overlay.on("mousemove", this.onMouseMove, this);
8247                 this.overlay.on("mouseup", this.onMouseUp, this);
8248             }
8249             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8250
8251             this.resizing = true;
8252             this.startBox = this.el.getBox();
8253             this.startPoint = e.getXY();
8254             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8255                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8256
8257             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8258             this.overlay.show();
8259
8260             if(this.constrainTo) {
8261                 var ct = Roo.get(this.constrainTo);
8262                 this.resizeRegion = ct.getRegion().adjust(
8263                     ct.getFrameWidth('t'),
8264                     ct.getFrameWidth('l'),
8265                     -ct.getFrameWidth('b'),
8266                     -ct.getFrameWidth('r')
8267                 );
8268             }
8269
8270             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8271             this.proxy.show();
8272             this.proxy.setBox(this.startBox);
8273             if(!this.dynamic){
8274                 this.proxy.setStyle('visibility', 'visible');
8275             }
8276         }
8277     },
8278
8279     // private
8280     onMouseDown : function(handle, e){
8281         if(this.enabled){
8282             e.stopEvent();
8283             this.activeHandle = handle;
8284             this.startSizing(e, handle);
8285         }
8286     },
8287
8288     // private
8289     onMouseUp : function(e){
8290         var size = this.resizeElement();
8291         this.resizing = false;
8292         this.handleOut();
8293         this.overlay.hide();
8294         this.proxy.hide();
8295         this.fireEvent("resize", this, size.width, size.height, e);
8296     },
8297
8298     // private
8299     updateChildSize : function(){
8300         
8301         if(this.resizeChild){
8302             var el = this.el;
8303             var child = this.resizeChild;
8304             var adj = this.adjustments;
8305             if(el.dom.offsetWidth){
8306                 var b = el.getSize(true);
8307                 child.setSize(b.width+adj[0], b.height+adj[1]);
8308             }
8309             // Second call here for IE
8310             // The first call enables instant resizing and
8311             // the second call corrects scroll bars if they
8312             // exist
8313             if(Roo.isIE){
8314                 setTimeout(function(){
8315                     if(el.dom.offsetWidth){
8316                         var b = el.getSize(true);
8317                         child.setSize(b.width+adj[0], b.height+adj[1]);
8318                     }
8319                 }, 10);
8320             }
8321         }
8322     },
8323
8324     // private
8325     snap : function(value, inc, min){
8326         if(!inc || !value) {
8327             return value;
8328         }
8329         var newValue = value;
8330         var m = value % inc;
8331         if(m > 0){
8332             if(m > (inc/2)){
8333                 newValue = value + (inc-m);
8334             }else{
8335                 newValue = value - m;
8336             }
8337         }
8338         return Math.max(min, newValue);
8339     },
8340
8341     // private
8342     resizeElement : function(){
8343         var box = this.proxy.getBox();
8344         if(this.updateBox){
8345             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8346         }else{
8347             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8348         }
8349         this.updateChildSize();
8350         if(!this.dynamic){
8351             this.proxy.hide();
8352         }
8353         return box;
8354     },
8355
8356     // private
8357     constrain : function(v, diff, m, mx){
8358         if(v - diff < m){
8359             diff = v - m;
8360         }else if(v - diff > mx){
8361             diff = mx - v;
8362         }
8363         return diff;
8364     },
8365
8366     // private
8367     onMouseMove : function(e){
8368         
8369         if(this.enabled){
8370             try{// try catch so if something goes wrong the user doesn't get hung
8371
8372             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8373                 return;
8374             }
8375
8376             //var curXY = this.startPoint;
8377             var curSize = this.curSize || this.startBox;
8378             var x = this.startBox.x, y = this.startBox.y;
8379             var ox = x, oy = y;
8380             var w = curSize.width, h = curSize.height;
8381             var ow = w, oh = h;
8382             var mw = this.minWidth, mh = this.minHeight;
8383             var mxw = this.maxWidth, mxh = this.maxHeight;
8384             var wi = this.widthIncrement;
8385             var hi = this.heightIncrement;
8386
8387             var eventXY = e.getXY();
8388             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8389             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8390
8391             var pos = this.activeHandle.position;
8392
8393             switch(pos){
8394                 case "east":
8395                     w += diffX;
8396                     w = Math.min(Math.max(mw, w), mxw);
8397                     break;
8398              
8399                 case "south":
8400                     h += diffY;
8401                     h = Math.min(Math.max(mh, h), mxh);
8402                     break;
8403                 case "southeast":
8404                     w += diffX;
8405                     h += diffY;
8406                     w = Math.min(Math.max(mw, w), mxw);
8407                     h = Math.min(Math.max(mh, h), mxh);
8408                     break;
8409                 case "north":
8410                     diffY = this.constrain(h, diffY, mh, mxh);
8411                     y += diffY;
8412                     h -= diffY;
8413                     break;
8414                 case "hdrag":
8415                     
8416                     if (wi) {
8417                         var adiffX = Math.abs(diffX);
8418                         var sub = (adiffX % wi); // how much 
8419                         if (sub > (wi/2)) { // far enough to snap
8420                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8421                         } else {
8422                             // remove difference.. 
8423                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8424                         }
8425                     }
8426                     x += diffX;
8427                     x = Math.max(this.minX, x);
8428                     break;
8429                 case "west":
8430                     diffX = this.constrain(w, diffX, mw, mxw);
8431                     x += diffX;
8432                     w -= diffX;
8433                     break;
8434                 case "northeast":
8435                     w += diffX;
8436                     w = Math.min(Math.max(mw, w), mxw);
8437                     diffY = this.constrain(h, diffY, mh, mxh);
8438                     y += diffY;
8439                     h -= diffY;
8440                     break;
8441                 case "northwest":
8442                     diffX = this.constrain(w, diffX, mw, mxw);
8443                     diffY = this.constrain(h, diffY, mh, mxh);
8444                     y += diffY;
8445                     h -= diffY;
8446                     x += diffX;
8447                     w -= diffX;
8448                     break;
8449                case "southwest":
8450                     diffX = this.constrain(w, diffX, mw, mxw);
8451                     h += diffY;
8452                     h = Math.min(Math.max(mh, h), mxh);
8453                     x += diffX;
8454                     w -= diffX;
8455                     break;
8456             }
8457
8458             var sw = this.snap(w, wi, mw);
8459             var sh = this.snap(h, hi, mh);
8460             if(sw != w || sh != h){
8461                 switch(pos){
8462                     case "northeast":
8463                         y -= sh - h;
8464                     break;
8465                     case "north":
8466                         y -= sh - h;
8467                         break;
8468                     case "southwest":
8469                         x -= sw - w;
8470                     break;
8471                     case "west":
8472                         x -= sw - w;
8473                         break;
8474                     case "northwest":
8475                         x -= sw - w;
8476                         y -= sh - h;
8477                     break;
8478                 }
8479                 w = sw;
8480                 h = sh;
8481             }
8482
8483             if(this.preserveRatio){
8484                 switch(pos){
8485                     case "southeast":
8486                     case "east":
8487                         h = oh * (w/ow);
8488                         h = Math.min(Math.max(mh, h), mxh);
8489                         w = ow * (h/oh);
8490                        break;
8491                     case "south":
8492                         w = ow * (h/oh);
8493                         w = Math.min(Math.max(mw, w), mxw);
8494                         h = oh * (w/ow);
8495                         break;
8496                     case "northeast":
8497                         w = ow * (h/oh);
8498                         w = Math.min(Math.max(mw, w), mxw);
8499                         h = oh * (w/ow);
8500                     break;
8501                     case "north":
8502                         var tw = w;
8503                         w = ow * (h/oh);
8504                         w = Math.min(Math.max(mw, w), mxw);
8505                         h = oh * (w/ow);
8506                         x += (tw - w) / 2;
8507                         break;
8508                     case "southwest":
8509                         h = oh * (w/ow);
8510                         h = Math.min(Math.max(mh, h), mxh);
8511                         var tw = w;
8512                         w = ow * (h/oh);
8513                         x += tw - w;
8514                         break;
8515                     case "west":
8516                         var th = h;
8517                         h = oh * (w/ow);
8518                         h = Math.min(Math.max(mh, h), mxh);
8519                         y += (th - h) / 2;
8520                         var tw = w;
8521                         w = ow * (h/oh);
8522                         x += tw - w;
8523                        break;
8524                     case "northwest":
8525                         var tw = w;
8526                         var th = h;
8527                         h = oh * (w/ow);
8528                         h = Math.min(Math.max(mh, h), mxh);
8529                         w = ow * (h/oh);
8530                         y += th - h;
8531                         x += tw - w;
8532                        break;
8533
8534                 }
8535             }
8536             if (pos == 'hdrag') {
8537                 w = ow;
8538             }
8539             this.proxy.setBounds(x, y, w, h);
8540             if(this.dynamic){
8541                 this.resizeElement();
8542             }
8543             }catch(e){}
8544         }
8545         this.fireEvent("resizing", this, x, y, w, h, e);
8546     },
8547
8548     // private
8549     handleOver : function(){
8550         if(this.enabled){
8551             this.el.addClass("x-resizable-over");
8552         }
8553     },
8554
8555     // private
8556     handleOut : function(){
8557         if(!this.resizing){
8558             this.el.removeClass("x-resizable-over");
8559         }
8560     },
8561
8562     /**
8563      * Returns the element this component is bound to.
8564      * @return {Roo.Element}
8565      */
8566     getEl : function(){
8567         return this.el;
8568     },
8569
8570     /**
8571      * Returns the resizeChild element (or null).
8572      * @return {Roo.Element}
8573      */
8574     getResizeChild : function(){
8575         return this.resizeChild;
8576     },
8577     groupHandler : function()
8578     {
8579         
8580     },
8581     /**
8582      * Destroys this resizable. If the element was wrapped and
8583      * removeEl is not true then the element remains.
8584      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8585      */
8586     destroy : function(removeEl){
8587         this.proxy.remove();
8588         if(this.overlay){
8589             this.overlay.removeAllListeners();
8590             this.overlay.remove();
8591         }
8592         var ps = Roo.Resizable.positions;
8593         for(var k in ps){
8594             if(typeof ps[k] != "function" && this[ps[k]]){
8595                 var h = this[ps[k]];
8596                 h.el.removeAllListeners();
8597                 h.el.remove();
8598             }
8599         }
8600         if(removeEl){
8601             this.el.update("");
8602             this.el.remove();
8603         }
8604     }
8605 });
8606
8607 // private
8608 // hash to map config positions to true positions
8609 Roo.Resizable.positions = {
8610     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8611     hd: "hdrag"
8612 };
8613
8614 // private
8615 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8616     if(!this.tpl){
8617         // only initialize the template if resizable is used
8618         var tpl = Roo.DomHelper.createTemplate(
8619             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8620         );
8621         tpl.compile();
8622         Roo.Resizable.Handle.prototype.tpl = tpl;
8623     }
8624     this.position = pos;
8625     this.rz = rz;
8626     // show north drag fro topdra
8627     var handlepos = pos == 'hdrag' ? 'north' : pos;
8628     
8629     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8630     if (pos == 'hdrag') {
8631         this.el.setStyle('cursor', 'pointer');
8632     }
8633     this.el.unselectable();
8634     if(transparent){
8635         this.el.setOpacity(0);
8636     }
8637     this.el.on("mousedown", this.onMouseDown, this);
8638     if(!disableTrackOver){
8639         this.el.on("mouseover", this.onMouseOver, this);
8640         this.el.on("mouseout", this.onMouseOut, this);
8641     }
8642 };
8643
8644 // private
8645 Roo.Resizable.Handle.prototype = {
8646     afterResize : function(rz){
8647         Roo.log('after?');
8648         // do nothing
8649     },
8650     // private
8651     onMouseDown : function(e){
8652         this.rz.onMouseDown(this, e);
8653     },
8654     // private
8655     onMouseOver : function(e){
8656         this.rz.handleOver(this, e);
8657     },
8658     // private
8659     onMouseOut : function(e){
8660         this.rz.handleOut(this, e);
8661     }
8662 };/*
8663  * Based on:
8664  * Ext JS Library 1.1.1
8665  * Copyright(c) 2006-2007, Ext JS, LLC.
8666  *
8667  * Originally Released Under LGPL - original licence link has changed is not relivant.
8668  *
8669  * Fork - LGPL
8670  * <script type="text/javascript">
8671  */
8672
8673 /**
8674  * @class Roo.Editor
8675  * @extends Roo.Component
8676  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8677  * @constructor
8678  * Create a new Editor
8679  * @param {Roo.form.Field} field The Field object (or descendant)
8680  * @param {Object} config The config object
8681  */
8682 Roo.Editor = function(field, config){
8683     Roo.Editor.superclass.constructor.call(this, config);
8684     this.field = field;
8685     this.addEvents({
8686         /**
8687              * @event beforestartedit
8688              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8689              * false from the handler of this event.
8690              * @param {Editor} this
8691              * @param {Roo.Element} boundEl The underlying element bound to this editor
8692              * @param {Mixed} value The field value being set
8693              */
8694         "beforestartedit" : true,
8695         /**
8696              * @event startedit
8697              * Fires when this editor is displayed
8698              * @param {Roo.Element} boundEl The underlying element bound to this editor
8699              * @param {Mixed} value The starting field value
8700              */
8701         "startedit" : true,
8702         /**
8703              * @event beforecomplete
8704              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8705              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8706              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8707              * event will not fire since no edit actually occurred.
8708              * @param {Editor} this
8709              * @param {Mixed} value The current field value
8710              * @param {Mixed} startValue The original field value
8711              */
8712         "beforecomplete" : true,
8713         /**
8714              * @event complete
8715              * Fires after editing is complete and any changed value has been written to the underlying field.
8716              * @param {Editor} this
8717              * @param {Mixed} value The current field value
8718              * @param {Mixed} startValue The original field value
8719              */
8720         "complete" : true,
8721         /**
8722          * @event specialkey
8723          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8724          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8725          * @param {Roo.form.Field} this
8726          * @param {Roo.EventObject} e The event object
8727          */
8728         "specialkey" : true
8729     });
8730 };
8731
8732 Roo.extend(Roo.Editor, Roo.Component, {
8733     /**
8734      * @cfg {Boolean/String} autosize
8735      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8736      * or "height" to adopt the height only (defaults to false)
8737      */
8738     /**
8739      * @cfg {Boolean} revertInvalid
8740      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8741      * validation fails (defaults to true)
8742      */
8743     /**
8744      * @cfg {Boolean} ignoreNoChange
8745      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8746      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8747      * will never be ignored.
8748      */
8749     /**
8750      * @cfg {Boolean} hideEl
8751      * False to keep the bound element visible while the editor is displayed (defaults to true)
8752      */
8753     /**
8754      * @cfg {Mixed} value
8755      * The data value of the underlying field (defaults to "")
8756      */
8757     value : "",
8758     /**
8759      * @cfg {String} alignment
8760      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8761      */
8762     alignment: "c-c?",
8763     /**
8764      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8765      * for bottom-right shadow (defaults to "frame")
8766      */
8767     shadow : "frame",
8768     /**
8769      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8770      */
8771     constrain : false,
8772     /**
8773      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8774      */
8775     completeOnEnter : false,
8776     /**
8777      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8778      */
8779     cancelOnEsc : false,
8780     /**
8781      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8782      */
8783     updateEl : false,
8784
8785     // private
8786     onRender : function(ct, position){
8787         this.el = new Roo.Layer({
8788             shadow: this.shadow,
8789             cls: "x-editor",
8790             parentEl : ct,
8791             shim : this.shim,
8792             shadowOffset:4,
8793             id: this.id,
8794             constrain: this.constrain
8795         });
8796         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8797         if(this.field.msgTarget != 'title'){
8798             this.field.msgTarget = 'qtip';
8799         }
8800         this.field.render(this.el);
8801         if(Roo.isGecko){
8802             this.field.el.dom.setAttribute('autocomplete', 'off');
8803         }
8804         this.field.on("specialkey", this.onSpecialKey, this);
8805         if(this.swallowKeys){
8806             this.field.el.swallowEvent(['keydown','keypress']);
8807         }
8808         this.field.show();
8809         this.field.on("blur", this.onBlur, this);
8810         if(this.field.grow){
8811             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8812         }
8813     },
8814
8815     onSpecialKey : function(field, e)
8816     {
8817         //Roo.log('editor onSpecialKey');
8818         if(this.completeOnEnter && e.getKey() == e.ENTER){
8819             e.stopEvent();
8820             this.completeEdit();
8821             return;
8822         }
8823         // do not fire special key otherwise it might hide close the editor...
8824         if(e.getKey() == e.ENTER){    
8825             return;
8826         }
8827         if(this.cancelOnEsc && e.getKey() == e.ESC){
8828             this.cancelEdit();
8829             return;
8830         } 
8831         this.fireEvent('specialkey', field, e);
8832     
8833     },
8834
8835     /**
8836      * Starts the editing process and shows the editor.
8837      * @param {String/HTMLElement/Element} el The element to edit
8838      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8839       * to the innerHTML of el.
8840      */
8841     startEdit : function(el, value){
8842         if(this.editing){
8843             this.completeEdit();
8844         }
8845         this.boundEl = Roo.get(el);
8846         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8847         if(!this.rendered){
8848             this.render(this.parentEl || document.body);
8849         }
8850         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8851             return;
8852         }
8853         this.startValue = v;
8854         this.field.setValue(v);
8855         if(this.autoSize){
8856             var sz = this.boundEl.getSize();
8857             switch(this.autoSize){
8858                 case "width":
8859                 this.setSize(sz.width,  "");
8860                 break;
8861                 case "height":
8862                 this.setSize("",  sz.height);
8863                 break;
8864                 default:
8865                 this.setSize(sz.width,  sz.height);
8866             }
8867         }
8868         this.el.alignTo(this.boundEl, this.alignment);
8869         this.editing = true;
8870         if(Roo.QuickTips){
8871             Roo.QuickTips.disable();
8872         }
8873         this.show();
8874     },
8875
8876     /**
8877      * Sets the height and width of this editor.
8878      * @param {Number} width The new width
8879      * @param {Number} height The new height
8880      */
8881     setSize : function(w, h){
8882         this.field.setSize(w, h);
8883         if(this.el){
8884             this.el.sync();
8885         }
8886     },
8887
8888     /**
8889      * Realigns the editor to the bound field based on the current alignment config value.
8890      */
8891     realign : function(){
8892         this.el.alignTo(this.boundEl, this.alignment);
8893     },
8894
8895     /**
8896      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8897      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8898      */
8899     completeEdit : function(remainVisible){
8900         if(!this.editing){
8901             return;
8902         }
8903         var v = this.getValue();
8904         if(this.revertInvalid !== false && !this.field.isValid()){
8905             v = this.startValue;
8906             this.cancelEdit(true);
8907         }
8908         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8909             this.editing = false;
8910             this.hide();
8911             return;
8912         }
8913         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8914             this.editing = false;
8915             if(this.updateEl && this.boundEl){
8916                 this.boundEl.update(v);
8917             }
8918             if(remainVisible !== true){
8919                 this.hide();
8920             }
8921             this.fireEvent("complete", this, v, this.startValue);
8922         }
8923     },
8924
8925     // private
8926     onShow : function(){
8927         this.el.show();
8928         if(this.hideEl !== false){
8929             this.boundEl.hide();
8930         }
8931         this.field.show();
8932         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8933             this.fixIEFocus = true;
8934             this.deferredFocus.defer(50, this);
8935         }else{
8936             this.field.focus();
8937         }
8938         this.fireEvent("startedit", this.boundEl, this.startValue);
8939     },
8940
8941     deferredFocus : function(){
8942         if(this.editing){
8943             this.field.focus();
8944         }
8945     },
8946
8947     /**
8948      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8949      * reverted to the original starting value.
8950      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8951      * cancel (defaults to false)
8952      */
8953     cancelEdit : function(remainVisible){
8954         if(this.editing){
8955             this.setValue(this.startValue);
8956             if(remainVisible !== true){
8957                 this.hide();
8958             }
8959         }
8960     },
8961
8962     // private
8963     onBlur : function(){
8964         if(this.allowBlur !== true && this.editing){
8965             this.completeEdit();
8966         }
8967     },
8968
8969     // private
8970     onHide : function(){
8971         if(this.editing){
8972             this.completeEdit();
8973             return;
8974         }
8975         this.field.blur();
8976         if(this.field.collapse){
8977             this.field.collapse();
8978         }
8979         this.el.hide();
8980         if(this.hideEl !== false){
8981             this.boundEl.show();
8982         }
8983         if(Roo.QuickTips){
8984             Roo.QuickTips.enable();
8985         }
8986     },
8987
8988     /**
8989      * Sets the data value of the editor
8990      * @param {Mixed} value Any valid value supported by the underlying field
8991      */
8992     setValue : function(v){
8993         this.field.setValue(v);
8994     },
8995
8996     /**
8997      * Gets the data value of the editor
8998      * @return {Mixed} The data value
8999      */
9000     getValue : function(){
9001         return this.field.getValue();
9002     }
9003 });/*
9004  * Based on:
9005  * Ext JS Library 1.1.1
9006  * Copyright(c) 2006-2007, Ext JS, LLC.
9007  *
9008  * Originally Released Under LGPL - original licence link has changed is not relivant.
9009  *
9010  * Fork - LGPL
9011  * <script type="text/javascript">
9012  */
9013  
9014 /**
9015  * @class Roo.BasicDialog
9016  * @extends Roo.util.Observable
9017  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9018  * <pre><code>
9019 var dlg = new Roo.BasicDialog("my-dlg", {
9020     height: 200,
9021     width: 300,
9022     minHeight: 100,
9023     minWidth: 150,
9024     modal: true,
9025     proxyDrag: true,
9026     shadow: true
9027 });
9028 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9029 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9030 dlg.addButton('Cancel', dlg.hide, dlg);
9031 dlg.show();
9032 </code></pre>
9033   <b>A Dialog should always be a direct child of the body element.</b>
9034  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9035  * @cfg {String} title Default text to display in the title bar (defaults to null)
9036  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9037  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9038  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9039  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9040  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9041  * (defaults to null with no animation)
9042  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9043  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9044  * property for valid values (defaults to 'all')
9045  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9046  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9047  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9048  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9049  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9050  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9051  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9052  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9053  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9054  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9055  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9056  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9057  * draggable = true (defaults to false)
9058  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9059  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9060  * shadow (defaults to false)
9061  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9062  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9063  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9064  * @cfg {Array} buttons Array of buttons
9065  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9066  * @constructor
9067  * Create a new BasicDialog.
9068  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9069  * @param {Object} config Configuration options
9070  */
9071 Roo.BasicDialog = function(el, config){
9072     this.el = Roo.get(el);
9073     var dh = Roo.DomHelper;
9074     if(!this.el && config && config.autoCreate){
9075         if(typeof config.autoCreate == "object"){
9076             if(!config.autoCreate.id){
9077                 config.autoCreate.id = el;
9078             }
9079             this.el = dh.append(document.body,
9080                         config.autoCreate, true);
9081         }else{
9082             this.el = dh.append(document.body,
9083                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9084         }
9085     }
9086     el = this.el;
9087     el.setDisplayed(true);
9088     el.hide = this.hideAction;
9089     this.id = el.id;
9090     el.addClass("x-dlg");
9091
9092     Roo.apply(this, config);
9093
9094     this.proxy = el.createProxy("x-dlg-proxy");
9095     this.proxy.hide = this.hideAction;
9096     this.proxy.setOpacity(.5);
9097     this.proxy.hide();
9098
9099     if(config.width){
9100         el.setWidth(config.width);
9101     }
9102     if(config.height){
9103         el.setHeight(config.height);
9104     }
9105     this.size = el.getSize();
9106     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9107         this.xy = [config.x,config.y];
9108     }else{
9109         this.xy = el.getCenterXY(true);
9110     }
9111     /** The header element @type Roo.Element */
9112     this.header = el.child("> .x-dlg-hd");
9113     /** The body element @type Roo.Element */
9114     this.body = el.child("> .x-dlg-bd");
9115     /** The footer element @type Roo.Element */
9116     this.footer = el.child("> .x-dlg-ft");
9117
9118     if(!this.header){
9119         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9120     }
9121     if(!this.body){
9122         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9123     }
9124
9125     this.header.unselectable();
9126     if(this.title){
9127         this.header.update(this.title);
9128     }
9129     // this element allows the dialog to be focused for keyboard event
9130     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9131     this.focusEl.swallowEvent("click", true);
9132
9133     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9134
9135     // wrap the body and footer for special rendering
9136     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9137     if(this.footer){
9138         this.bwrap.dom.appendChild(this.footer.dom);
9139     }
9140
9141     this.bg = this.el.createChild({
9142         tag: "div", cls:"x-dlg-bg",
9143         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9144     });
9145     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9146
9147
9148     if(this.autoScroll !== false && !this.autoTabs){
9149         this.body.setStyle("overflow", "auto");
9150     }
9151
9152     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9153
9154     if(this.closable !== false){
9155         this.el.addClass("x-dlg-closable");
9156         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9157         this.close.on("click", this.closeClick, this);
9158         this.close.addClassOnOver("x-dlg-close-over");
9159     }
9160     if(this.collapsible !== false){
9161         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9162         this.collapseBtn.on("click", this.collapseClick, this);
9163         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9164         this.header.on("dblclick", this.collapseClick, this);
9165     }
9166     if(this.resizable !== false){
9167         this.el.addClass("x-dlg-resizable");
9168         this.resizer = new Roo.Resizable(el, {
9169             minWidth: this.minWidth || 80,
9170             minHeight:this.minHeight || 80,
9171             handles: this.resizeHandles || "all",
9172             pinned: true
9173         });
9174         this.resizer.on("beforeresize", this.beforeResize, this);
9175         this.resizer.on("resize", this.onResize, this);
9176     }
9177     if(this.draggable !== false){
9178         el.addClass("x-dlg-draggable");
9179         if (!this.proxyDrag) {
9180             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9181         }
9182         else {
9183             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9184         }
9185         dd.setHandleElId(this.header.id);
9186         dd.endDrag = this.endMove.createDelegate(this);
9187         dd.startDrag = this.startMove.createDelegate(this);
9188         dd.onDrag = this.onDrag.createDelegate(this);
9189         dd.scroll = false;
9190         this.dd = dd;
9191     }
9192     if(this.modal){
9193         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9194         this.mask.enableDisplayMode("block");
9195         this.mask.hide();
9196         this.el.addClass("x-dlg-modal");
9197     }
9198     if(this.shadow){
9199         this.shadow = new Roo.Shadow({
9200             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9201             offset : this.shadowOffset
9202         });
9203     }else{
9204         this.shadowOffset = 0;
9205     }
9206     if(Roo.useShims && this.shim !== false){
9207         this.shim = this.el.createShim();
9208         this.shim.hide = this.hideAction;
9209         this.shim.hide();
9210     }else{
9211         this.shim = false;
9212     }
9213     if(this.autoTabs){
9214         this.initTabs();
9215     }
9216     if (this.buttons) { 
9217         var bts= this.buttons;
9218         this.buttons = [];
9219         Roo.each(bts, function(b) {
9220             this.addButton(b);
9221         }, this);
9222     }
9223     
9224     
9225     this.addEvents({
9226         /**
9227          * @event keydown
9228          * Fires when a key is pressed
9229          * @param {Roo.BasicDialog} this
9230          * @param {Roo.EventObject} e
9231          */
9232         "keydown" : true,
9233         /**
9234          * @event move
9235          * Fires when this dialog is moved by the user.
9236          * @param {Roo.BasicDialog} this
9237          * @param {Number} x The new page X
9238          * @param {Number} y The new page Y
9239          */
9240         "move" : true,
9241         /**
9242          * @event resize
9243          * Fires when this dialog is resized by the user.
9244          * @param {Roo.BasicDialog} this
9245          * @param {Number} width The new width
9246          * @param {Number} height The new height
9247          */
9248         "resize" : true,
9249         /**
9250          * @event beforehide
9251          * Fires before this dialog is hidden.
9252          * @param {Roo.BasicDialog} this
9253          */
9254         "beforehide" : true,
9255         /**
9256          * @event hide
9257          * Fires when this dialog is hidden.
9258          * @param {Roo.BasicDialog} this
9259          */
9260         "hide" : true,
9261         /**
9262          * @event beforeshow
9263          * Fires before this dialog is shown.
9264          * @param {Roo.BasicDialog} this
9265          */
9266         "beforeshow" : true,
9267         /**
9268          * @event show
9269          * Fires when this dialog is shown.
9270          * @param {Roo.BasicDialog} this
9271          */
9272         "show" : true
9273     });
9274     el.on("keydown", this.onKeyDown, this);
9275     el.on("mousedown", this.toFront, this);
9276     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9277     this.el.hide();
9278     Roo.DialogManager.register(this);
9279     Roo.BasicDialog.superclass.constructor.call(this);
9280 };
9281
9282 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9283     shadowOffset: Roo.isIE ? 6 : 5,
9284     minHeight: 80,
9285     minWidth: 200,
9286     minButtonWidth: 75,
9287     defaultButton: null,
9288     buttonAlign: "right",
9289     tabTag: 'div',
9290     firstShow: true,
9291
9292     /**
9293      * Sets the dialog title text
9294      * @param {String} text The title text to display
9295      * @return {Roo.BasicDialog} this
9296      */
9297     setTitle : function(text){
9298         this.header.update(text);
9299         return this;
9300     },
9301
9302     // private
9303     closeClick : function(){
9304         this.hide();
9305     },
9306
9307     // private
9308     collapseClick : function(){
9309         this[this.collapsed ? "expand" : "collapse"]();
9310     },
9311
9312     /**
9313      * Collapses the dialog to its minimized state (only the title bar is visible).
9314      * Equivalent to the user clicking the collapse dialog button.
9315      */
9316     collapse : function(){
9317         if(!this.collapsed){
9318             this.collapsed = true;
9319             this.el.addClass("x-dlg-collapsed");
9320             this.restoreHeight = this.el.getHeight();
9321             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9322         }
9323     },
9324
9325     /**
9326      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9327      * clicking the expand dialog button.
9328      */
9329     expand : function(){
9330         if(this.collapsed){
9331             this.collapsed = false;
9332             this.el.removeClass("x-dlg-collapsed");
9333             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9334         }
9335     },
9336
9337     /**
9338      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9339      * @return {Roo.TabPanel} The tabs component
9340      */
9341     initTabs : function(){
9342         var tabs = this.getTabs();
9343         while(tabs.getTab(0)){
9344             tabs.removeTab(0);
9345         }
9346         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9347             var dom = el.dom;
9348             tabs.addTab(Roo.id(dom), dom.title);
9349             dom.title = "";
9350         });
9351         tabs.activate(0);
9352         return tabs;
9353     },
9354
9355     // private
9356     beforeResize : function(){
9357         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9358     },
9359
9360     // private
9361     onResize : function(){
9362         this.refreshSize();
9363         this.syncBodyHeight();
9364         this.adjustAssets();
9365         this.focus();
9366         this.fireEvent("resize", this, this.size.width, this.size.height);
9367     },
9368
9369     // private
9370     onKeyDown : function(e){
9371         if(this.isVisible()){
9372             this.fireEvent("keydown", this, e);
9373         }
9374     },
9375
9376     /**
9377      * Resizes the dialog.
9378      * @param {Number} width
9379      * @param {Number} height
9380      * @return {Roo.BasicDialog} this
9381      */
9382     resizeTo : function(width, height){
9383         this.el.setSize(width, height);
9384         this.size = {width: width, height: height};
9385         this.syncBodyHeight();
9386         if(this.fixedcenter){
9387             this.center();
9388         }
9389         if(this.isVisible()){
9390             this.constrainXY();
9391             this.adjustAssets();
9392         }
9393         this.fireEvent("resize", this, width, height);
9394         return this;
9395     },
9396
9397
9398     /**
9399      * Resizes the dialog to fit the specified content size.
9400      * @param {Number} width
9401      * @param {Number} height
9402      * @return {Roo.BasicDialog} this
9403      */
9404     setContentSize : function(w, h){
9405         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9406         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9407         //if(!this.el.isBorderBox()){
9408             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9409             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9410         //}
9411         if(this.tabs){
9412             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9413             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9414         }
9415         this.resizeTo(w, h);
9416         return this;
9417     },
9418
9419     /**
9420      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9421      * executed in response to a particular key being pressed while the dialog is active.
9422      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9423      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9424      * @param {Function} fn The function to call
9425      * @param {Object} scope (optional) The scope of the function
9426      * @return {Roo.BasicDialog} this
9427      */
9428     addKeyListener : function(key, fn, scope){
9429         var keyCode, shift, ctrl, alt;
9430         if(typeof key == "object" && !(key instanceof Array)){
9431             keyCode = key["key"];
9432             shift = key["shift"];
9433             ctrl = key["ctrl"];
9434             alt = key["alt"];
9435         }else{
9436             keyCode = key;
9437         }
9438         var handler = function(dlg, e){
9439             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9440                 var k = e.getKey();
9441                 if(keyCode instanceof Array){
9442                     for(var i = 0, len = keyCode.length; i < len; i++){
9443                         if(keyCode[i] == k){
9444                           fn.call(scope || window, dlg, k, e);
9445                           return;
9446                         }
9447                     }
9448                 }else{
9449                     if(k == keyCode){
9450                         fn.call(scope || window, dlg, k, e);
9451                     }
9452                 }
9453             }
9454         };
9455         this.on("keydown", handler);
9456         return this;
9457     },
9458
9459     /**
9460      * Returns the TabPanel component (creates it if it doesn't exist).
9461      * Note: If you wish to simply check for the existence of tabs without creating them,
9462      * check for a null 'tabs' property.
9463      * @return {Roo.TabPanel} The tabs component
9464      */
9465     getTabs : function(){
9466         if(!this.tabs){
9467             this.el.addClass("x-dlg-auto-tabs");
9468             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9469             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9470         }
9471         return this.tabs;
9472     },
9473
9474     /**
9475      * Adds a button to the footer section of the dialog.
9476      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9477      * object or a valid Roo.DomHelper element config
9478      * @param {Function} handler The function called when the button is clicked
9479      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9480      * @return {Roo.Button} The new button
9481      */
9482     addButton : function(config, handler, scope){
9483         var dh = Roo.DomHelper;
9484         if(!this.footer){
9485             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9486         }
9487         if(!this.btnContainer){
9488             var tb = this.footer.createChild({
9489
9490                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9491                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9492             }, null, true);
9493             this.btnContainer = tb.firstChild.firstChild.firstChild;
9494         }
9495         var bconfig = {
9496             handler: handler,
9497             scope: scope,
9498             minWidth: this.minButtonWidth,
9499             hideParent:true
9500         };
9501         if(typeof config == "string"){
9502             bconfig.text = config;
9503         }else{
9504             if(config.tag){
9505                 bconfig.dhconfig = config;
9506             }else{
9507                 Roo.apply(bconfig, config);
9508             }
9509         }
9510         var fc = false;
9511         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9512             bconfig.position = Math.max(0, bconfig.position);
9513             fc = this.btnContainer.childNodes[bconfig.position];
9514         }
9515          
9516         var btn = new Roo.Button(
9517             fc ? 
9518                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9519                 : this.btnContainer.appendChild(document.createElement("td")),
9520             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9521             bconfig
9522         );
9523         this.syncBodyHeight();
9524         if(!this.buttons){
9525             /**
9526              * Array of all the buttons that have been added to this dialog via addButton
9527              * @type Array
9528              */
9529             this.buttons = [];
9530         }
9531         this.buttons.push(btn);
9532         return btn;
9533     },
9534
9535     /**
9536      * Sets the default button to be focused when the dialog is displayed.
9537      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9538      * @return {Roo.BasicDialog} this
9539      */
9540     setDefaultButton : function(btn){
9541         this.defaultButton = btn;
9542         return this;
9543     },
9544
9545     // private
9546     getHeaderFooterHeight : function(safe){
9547         var height = 0;
9548         if(this.header){
9549            height += this.header.getHeight();
9550         }
9551         if(this.footer){
9552            var fm = this.footer.getMargins();
9553             height += (this.footer.getHeight()+fm.top+fm.bottom);
9554         }
9555         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9556         height += this.centerBg.getPadding("tb");
9557         return height;
9558     },
9559
9560     // private
9561     syncBodyHeight : function()
9562     {
9563         var bd = this.body, // the text
9564             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9565             bw = this.bwrap;
9566         var height = this.size.height - this.getHeaderFooterHeight(false);
9567         bd.setHeight(height-bd.getMargins("tb"));
9568         var hh = this.header.getHeight();
9569         var h = this.size.height-hh;
9570         cb.setHeight(h);
9571         
9572         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9573         bw.setHeight(h-cb.getPadding("tb"));
9574         
9575         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9576         bd.setWidth(bw.getWidth(true));
9577         if(this.tabs){
9578             this.tabs.syncHeight();
9579             if(Roo.isIE){
9580                 this.tabs.el.repaint();
9581             }
9582         }
9583     },
9584
9585     /**
9586      * Restores the previous state of the dialog if Roo.state is configured.
9587      * @return {Roo.BasicDialog} this
9588      */
9589     restoreState : function(){
9590         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9591         if(box && box.width){
9592             this.xy = [box.x, box.y];
9593             this.resizeTo(box.width, box.height);
9594         }
9595         return this;
9596     },
9597
9598     // private
9599     beforeShow : function(){
9600         this.expand();
9601         if(this.fixedcenter){
9602             this.xy = this.el.getCenterXY(true);
9603         }
9604         if(this.modal){
9605             Roo.get(document.body).addClass("x-body-masked");
9606             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9607             this.mask.show();
9608         }
9609         this.constrainXY();
9610     },
9611
9612     // private
9613     animShow : function(){
9614         var b = Roo.get(this.animateTarget).getBox();
9615         this.proxy.setSize(b.width, b.height);
9616         this.proxy.setLocation(b.x, b.y);
9617         this.proxy.show();
9618         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9619                     true, .35, this.showEl.createDelegate(this));
9620     },
9621
9622     /**
9623      * Shows the dialog.
9624      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9625      * @return {Roo.BasicDialog} this
9626      */
9627     show : function(animateTarget){
9628         if (this.fireEvent("beforeshow", this) === false){
9629             return;
9630         }
9631         if(this.syncHeightBeforeShow){
9632             this.syncBodyHeight();
9633         }else if(this.firstShow){
9634             this.firstShow = false;
9635             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9636         }
9637         this.animateTarget = animateTarget || this.animateTarget;
9638         if(!this.el.isVisible()){
9639             this.beforeShow();
9640             if(this.animateTarget && Roo.get(this.animateTarget)){
9641                 this.animShow();
9642             }else{
9643                 this.showEl();
9644             }
9645         }
9646         return this;
9647     },
9648
9649     // private
9650     showEl : function(){
9651         this.proxy.hide();
9652         this.el.setXY(this.xy);
9653         this.el.show();
9654         this.adjustAssets(true);
9655         this.toFront();
9656         this.focus();
9657         // IE peekaboo bug - fix found by Dave Fenwick
9658         if(Roo.isIE){
9659             this.el.repaint();
9660         }
9661         this.fireEvent("show", this);
9662     },
9663
9664     /**
9665      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9666      * dialog itself will receive focus.
9667      */
9668     focus : function(){
9669         if(this.defaultButton){
9670             this.defaultButton.focus();
9671         }else{
9672             this.focusEl.focus();
9673         }
9674     },
9675
9676     // private
9677     constrainXY : function(){
9678         if(this.constraintoviewport !== false){
9679             if(!this.viewSize){
9680                 if(this.container){
9681                     var s = this.container.getSize();
9682                     this.viewSize = [s.width, s.height];
9683                 }else{
9684                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9685                 }
9686             }
9687             var s = Roo.get(this.container||document).getScroll();
9688
9689             var x = this.xy[0], y = this.xy[1];
9690             var w = this.size.width, h = this.size.height;
9691             var vw = this.viewSize[0], vh = this.viewSize[1];
9692             // only move it if it needs it
9693             var moved = false;
9694             // first validate right/bottom
9695             if(x + w > vw+s.left){
9696                 x = vw - w;
9697                 moved = true;
9698             }
9699             if(y + h > vh+s.top){
9700                 y = vh - h;
9701                 moved = true;
9702             }
9703             // then make sure top/left isn't negative
9704             if(x < s.left){
9705                 x = s.left;
9706                 moved = true;
9707             }
9708             if(y < s.top){
9709                 y = s.top;
9710                 moved = true;
9711             }
9712             if(moved){
9713                 // cache xy
9714                 this.xy = [x, y];
9715                 if(this.isVisible()){
9716                     this.el.setLocation(x, y);
9717                     this.adjustAssets();
9718                 }
9719             }
9720         }
9721     },
9722
9723     // private
9724     onDrag : function(){
9725         if(!this.proxyDrag){
9726             this.xy = this.el.getXY();
9727             this.adjustAssets();
9728         }
9729     },
9730
9731     // private
9732     adjustAssets : function(doShow){
9733         var x = this.xy[0], y = this.xy[1];
9734         var w = this.size.width, h = this.size.height;
9735         if(doShow === true){
9736             if(this.shadow){
9737                 this.shadow.show(this.el);
9738             }
9739             if(this.shim){
9740                 this.shim.show();
9741             }
9742         }
9743         if(this.shadow && this.shadow.isVisible()){
9744             this.shadow.show(this.el);
9745         }
9746         if(this.shim && this.shim.isVisible()){
9747             this.shim.setBounds(x, y, w, h);
9748         }
9749     },
9750
9751     // private
9752     adjustViewport : function(w, h){
9753         if(!w || !h){
9754             w = Roo.lib.Dom.getViewWidth();
9755             h = Roo.lib.Dom.getViewHeight();
9756         }
9757         // cache the size
9758         this.viewSize = [w, h];
9759         if(this.modal && this.mask.isVisible()){
9760             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9761             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9762         }
9763         if(this.isVisible()){
9764             this.constrainXY();
9765         }
9766     },
9767
9768     /**
9769      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9770      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9771      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9772      */
9773     destroy : function(removeEl){
9774         if(this.isVisible()){
9775             this.animateTarget = null;
9776             this.hide();
9777         }
9778         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9779         if(this.tabs){
9780             this.tabs.destroy(removeEl);
9781         }
9782         Roo.destroy(
9783              this.shim,
9784              this.proxy,
9785              this.resizer,
9786              this.close,
9787              this.mask
9788         );
9789         if(this.dd){
9790             this.dd.unreg();
9791         }
9792         if(this.buttons){
9793            for(var i = 0, len = this.buttons.length; i < len; i++){
9794                this.buttons[i].destroy();
9795            }
9796         }
9797         this.el.removeAllListeners();
9798         if(removeEl === true){
9799             this.el.update("");
9800             this.el.remove();
9801         }
9802         Roo.DialogManager.unregister(this);
9803     },
9804
9805     // private
9806     startMove : function(){
9807         if(this.proxyDrag){
9808             this.proxy.show();
9809         }
9810         if(this.constraintoviewport !== false){
9811             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9812         }
9813     },
9814
9815     // private
9816     endMove : function(){
9817         if(!this.proxyDrag){
9818             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9819         }else{
9820             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9821             this.proxy.hide();
9822         }
9823         this.refreshSize();
9824         this.adjustAssets();
9825         this.focus();
9826         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9827     },
9828
9829     /**
9830      * Brings this dialog to the front of any other visible dialogs
9831      * @return {Roo.BasicDialog} this
9832      */
9833     toFront : function(){
9834         Roo.DialogManager.bringToFront(this);
9835         return this;
9836     },
9837
9838     /**
9839      * Sends this dialog to the back (under) of any other visible dialogs
9840      * @return {Roo.BasicDialog} this
9841      */
9842     toBack : function(){
9843         Roo.DialogManager.sendToBack(this);
9844         return this;
9845     },
9846
9847     /**
9848      * Centers this dialog in the viewport
9849      * @return {Roo.BasicDialog} this
9850      */
9851     center : function(){
9852         var xy = this.el.getCenterXY(true);
9853         this.moveTo(xy[0], xy[1]);
9854         return this;
9855     },
9856
9857     /**
9858      * Moves the dialog's top-left corner to the specified point
9859      * @param {Number} x
9860      * @param {Number} y
9861      * @return {Roo.BasicDialog} this
9862      */
9863     moveTo : function(x, y){
9864         this.xy = [x,y];
9865         if(this.isVisible()){
9866             this.el.setXY(this.xy);
9867             this.adjustAssets();
9868         }
9869         return this;
9870     },
9871
9872     /**
9873      * Aligns the dialog to the specified element
9874      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9875      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9876      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9877      * @return {Roo.BasicDialog} this
9878      */
9879     alignTo : function(element, position, offsets){
9880         this.xy = this.el.getAlignToXY(element, position, offsets);
9881         if(this.isVisible()){
9882             this.el.setXY(this.xy);
9883             this.adjustAssets();
9884         }
9885         return this;
9886     },
9887
9888     /**
9889      * Anchors an element to another element and realigns it when the window is resized.
9890      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9891      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9892      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9893      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9894      * is a number, it is used as the buffer delay (defaults to 50ms).
9895      * @return {Roo.BasicDialog} this
9896      */
9897     anchorTo : function(el, alignment, offsets, monitorScroll){
9898         var action = function(){
9899             this.alignTo(el, alignment, offsets);
9900         };
9901         Roo.EventManager.onWindowResize(action, this);
9902         var tm = typeof monitorScroll;
9903         if(tm != 'undefined'){
9904             Roo.EventManager.on(window, 'scroll', action, this,
9905                 {buffer: tm == 'number' ? monitorScroll : 50});
9906         }
9907         action.call(this);
9908         return this;
9909     },
9910
9911     /**
9912      * Returns true if the dialog is visible
9913      * @return {Boolean}
9914      */
9915     isVisible : function(){
9916         return this.el.isVisible();
9917     },
9918
9919     // private
9920     animHide : function(callback){
9921         var b = Roo.get(this.animateTarget).getBox();
9922         this.proxy.show();
9923         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9924         this.el.hide();
9925         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9926                     this.hideEl.createDelegate(this, [callback]));
9927     },
9928
9929     /**
9930      * Hides the dialog.
9931      * @param {Function} callback (optional) Function to call when the dialog is hidden
9932      * @return {Roo.BasicDialog} this
9933      */
9934     hide : function(callback){
9935         if (this.fireEvent("beforehide", this) === false){
9936             return;
9937         }
9938         if(this.shadow){
9939             this.shadow.hide();
9940         }
9941         if(this.shim) {
9942           this.shim.hide();
9943         }
9944         // sometimes animateTarget seems to get set.. causing problems...
9945         // this just double checks..
9946         if(this.animateTarget && Roo.get(this.animateTarget)) {
9947            this.animHide(callback);
9948         }else{
9949             this.el.hide();
9950             this.hideEl(callback);
9951         }
9952         return this;
9953     },
9954
9955     // private
9956     hideEl : function(callback){
9957         this.proxy.hide();
9958         if(this.modal){
9959             this.mask.hide();
9960             Roo.get(document.body).removeClass("x-body-masked");
9961         }
9962         this.fireEvent("hide", this);
9963         if(typeof callback == "function"){
9964             callback();
9965         }
9966     },
9967
9968     // private
9969     hideAction : function(){
9970         this.setLeft("-10000px");
9971         this.setTop("-10000px");
9972         this.setStyle("visibility", "hidden");
9973     },
9974
9975     // private
9976     refreshSize : function(){
9977         this.size = this.el.getSize();
9978         this.xy = this.el.getXY();
9979         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9980     },
9981
9982     // private
9983     // z-index is managed by the DialogManager and may be overwritten at any time
9984     setZIndex : function(index){
9985         if(this.modal){
9986             this.mask.setStyle("z-index", index);
9987         }
9988         if(this.shim){
9989             this.shim.setStyle("z-index", ++index);
9990         }
9991         if(this.shadow){
9992             this.shadow.setZIndex(++index);
9993         }
9994         this.el.setStyle("z-index", ++index);
9995         if(this.proxy){
9996             this.proxy.setStyle("z-index", ++index);
9997         }
9998         if(this.resizer){
9999             this.resizer.proxy.setStyle("z-index", ++index);
10000         }
10001
10002         this.lastZIndex = index;
10003     },
10004
10005     /**
10006      * Returns the element for this dialog
10007      * @return {Roo.Element} The underlying dialog Element
10008      */
10009     getEl : function(){
10010         return this.el;
10011     }
10012 });
10013
10014 /**
10015  * @class Roo.DialogManager
10016  * Provides global access to BasicDialogs that have been created and
10017  * support for z-indexing (layering) multiple open dialogs.
10018  */
10019 Roo.DialogManager = function(){
10020     var list = {};
10021     var accessList = [];
10022     var front = null;
10023
10024     // private
10025     var sortDialogs = function(d1, d2){
10026         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10027     };
10028
10029     // private
10030     var orderDialogs = function(){
10031         accessList.sort(sortDialogs);
10032         var seed = Roo.DialogManager.zseed;
10033         for(var i = 0, len = accessList.length; i < len; i++){
10034             var dlg = accessList[i];
10035             if(dlg){
10036                 dlg.setZIndex(seed + (i*10));
10037             }
10038         }
10039     };
10040
10041     return {
10042         /**
10043          * The starting z-index for BasicDialogs (defaults to 9000)
10044          * @type Number The z-index value
10045          */
10046         zseed : 9000,
10047
10048         // private
10049         register : function(dlg){
10050             list[dlg.id] = dlg;
10051             accessList.push(dlg);
10052         },
10053
10054         // private
10055         unregister : function(dlg){
10056             delete list[dlg.id];
10057             var i=0;
10058             var len=0;
10059             if(!accessList.indexOf){
10060                 for(  i = 0, len = accessList.length; i < len; i++){
10061                     if(accessList[i] == dlg){
10062                         accessList.splice(i, 1);
10063                         return;
10064                     }
10065                 }
10066             }else{
10067                  i = accessList.indexOf(dlg);
10068                 if(i != -1){
10069                     accessList.splice(i, 1);
10070                 }
10071             }
10072         },
10073
10074         /**
10075          * Gets a registered dialog by id
10076          * @param {String/Object} id The id of the dialog or a dialog
10077          * @return {Roo.BasicDialog} this
10078          */
10079         get : function(id){
10080             return typeof id == "object" ? id : list[id];
10081         },
10082
10083         /**
10084          * Brings the specified dialog to the front
10085          * @param {String/Object} dlg The id of the dialog or a dialog
10086          * @return {Roo.BasicDialog} this
10087          */
10088         bringToFront : function(dlg){
10089             dlg = this.get(dlg);
10090             if(dlg != front){
10091                 front = dlg;
10092                 dlg._lastAccess = new Date().getTime();
10093                 orderDialogs();
10094             }
10095             return dlg;
10096         },
10097
10098         /**
10099          * Sends the specified dialog to the back
10100          * @param {String/Object} dlg The id of the dialog or a dialog
10101          * @return {Roo.BasicDialog} this
10102          */
10103         sendToBack : function(dlg){
10104             dlg = this.get(dlg);
10105             dlg._lastAccess = -(new Date().getTime());
10106             orderDialogs();
10107             return dlg;
10108         },
10109
10110         /**
10111          * Hides all dialogs
10112          */
10113         hideAll : function(){
10114             for(var id in list){
10115                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10116                     list[id].hide();
10117                 }
10118             }
10119         }
10120     };
10121 }();
10122
10123 /**
10124  * @class Roo.LayoutDialog
10125  * @extends Roo.BasicDialog
10126  * @children Roo.ContentPanel
10127  * @parent builder none
10128  * Dialog which provides adjustments for working with a layout in a Dialog.
10129  * Add your necessary layout config options to the dialog's config.<br>
10130  * Example usage (including a nested layout):
10131  * <pre><code>
10132 if(!dialog){
10133     dialog = new Roo.LayoutDialog("download-dlg", {
10134         modal: true,
10135         width:600,
10136         height:450,
10137         shadow:true,
10138         minWidth:500,
10139         minHeight:350,
10140         autoTabs:true,
10141         proxyDrag:true,
10142         // layout config merges with the dialog config
10143         center:{
10144             tabPosition: "top",
10145             alwaysShowTabs: true
10146         }
10147     });
10148     dialog.addKeyListener(27, dialog.hide, dialog);
10149     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10150     dialog.addButton("Build It!", this.getDownload, this);
10151
10152     // we can even add nested layouts
10153     var innerLayout = new Roo.BorderLayout("dl-inner", {
10154         east: {
10155             initialSize: 200,
10156             autoScroll:true,
10157             split:true
10158         },
10159         center: {
10160             autoScroll:true
10161         }
10162     });
10163     innerLayout.beginUpdate();
10164     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10165     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10166     innerLayout.endUpdate(true);
10167
10168     var layout = dialog.getLayout();
10169     layout.beginUpdate();
10170     layout.add("center", new Roo.ContentPanel("standard-panel",
10171                         {title: "Download the Source", fitToFrame:true}));
10172     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10173                {title: "Build your own roo.js"}));
10174     layout.getRegion("center").showPanel(sp);
10175     layout.endUpdate();
10176 }
10177 </code></pre>
10178     * @constructor
10179     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10180     * @param {Object} config configuration options
10181   */
10182 Roo.LayoutDialog = function(el, cfg){
10183     
10184     var config=  cfg;
10185     if (typeof(cfg) == 'undefined') {
10186         config = Roo.apply({}, el);
10187         // not sure why we use documentElement here.. - it should always be body.
10188         // IE7 borks horribly if we use documentElement.
10189         // webkit also does not like documentElement - it creates a body element...
10190         el = Roo.get( document.body || document.documentElement ).createChild();
10191         //config.autoCreate = true;
10192     }
10193     
10194     
10195     config.autoTabs = false;
10196     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10197     this.body.setStyle({overflow:"hidden", position:"relative"});
10198     this.layout = new Roo.BorderLayout(this.body.dom, config);
10199     this.layout.monitorWindowResize = false;
10200     this.el.addClass("x-dlg-auto-layout");
10201     // fix case when center region overwrites center function
10202     this.center = Roo.BasicDialog.prototype.center;
10203     this.on("show", this.layout.layout, this.layout, true);
10204     if (config.items) {
10205         var xitems = config.items;
10206         delete config.items;
10207         Roo.each(xitems, this.addxtype, this);
10208     }
10209     
10210     
10211 };
10212 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10213     
10214     
10215     /**
10216      * @cfg {Roo.LayoutRegion} east  
10217      */
10218     /**
10219      * @cfg {Roo.LayoutRegion} west
10220      */
10221     /**
10222      * @cfg {Roo.LayoutRegion} south
10223      */
10224     /**
10225      * @cfg {Roo.LayoutRegion} north
10226      */
10227     /**
10228      * @cfg {Roo.LayoutRegion} center
10229      */
10230     /**
10231      * @cfg {Roo.Button} buttons[]  Bottom buttons..
10232      */
10233     
10234     
10235     /**
10236      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10237      * @deprecated
10238      */
10239     endUpdate : function(){
10240         this.layout.endUpdate();
10241     },
10242
10243     /**
10244      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10245      *  @deprecated
10246      */
10247     beginUpdate : function(){
10248         this.layout.beginUpdate();
10249     },
10250
10251     /**
10252      * Get the BorderLayout for this dialog
10253      * @return {Roo.BorderLayout}
10254      */
10255     getLayout : function(){
10256         return this.layout;
10257     },
10258
10259     showEl : function(){
10260         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10261         if(Roo.isIE7){
10262             this.layout.layout();
10263         }
10264     },
10265
10266     // private
10267     // Use the syncHeightBeforeShow config option to control this automatically
10268     syncBodyHeight : function(){
10269         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10270         if(this.layout){this.layout.layout();}
10271     },
10272     
10273       /**
10274      * Add an xtype element (actually adds to the layout.)
10275      * @return {Object} xdata xtype object data.
10276      */
10277     
10278     addxtype : function(c) {
10279         return this.layout.addxtype(c);
10280     }
10281 });/*
10282  * Based on:
10283  * Ext JS Library 1.1.1
10284  * Copyright(c) 2006-2007, Ext JS, LLC.
10285  *
10286  * Originally Released Under LGPL - original licence link has changed is not relivant.
10287  *
10288  * Fork - LGPL
10289  * <script type="text/javascript">
10290  */
10291  
10292 /**
10293  * @class Roo.MessageBox
10294  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10295  * Example usage:
10296  *<pre><code>
10297 // Basic alert:
10298 Roo.Msg.alert('Status', 'Changes saved successfully.');
10299
10300 // Prompt for user data:
10301 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10302     if (btn == 'ok'){
10303         // process text value...
10304     }
10305 });
10306
10307 // Show a dialog using config options:
10308 Roo.Msg.show({
10309    title:'Save Changes?',
10310    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10311    buttons: Roo.Msg.YESNOCANCEL,
10312    fn: processResult,
10313    animEl: 'elId'
10314 });
10315 </code></pre>
10316  * @static
10317  */
10318 Roo.MessageBox = function(){
10319     var dlg, opt, mask, waitTimer;
10320     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10321     var buttons, activeTextEl, bwidth;
10322
10323     // private
10324     var handleButton = function(button){
10325         dlg.hide();
10326         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10327     };
10328
10329     // private
10330     var handleHide = function(){
10331         if(opt && opt.cls){
10332             dlg.el.removeClass(opt.cls);
10333         }
10334         if(waitTimer){
10335             Roo.TaskMgr.stop(waitTimer);
10336             waitTimer = null;
10337         }
10338     };
10339
10340     // private
10341     var updateButtons = function(b){
10342         var width = 0;
10343         if(!b){
10344             buttons["ok"].hide();
10345             buttons["cancel"].hide();
10346             buttons["yes"].hide();
10347             buttons["no"].hide();
10348             dlg.footer.dom.style.display = 'none';
10349             return width;
10350         }
10351         dlg.footer.dom.style.display = '';
10352         for(var k in buttons){
10353             if(typeof buttons[k] != "function"){
10354                 if(b[k]){
10355                     buttons[k].show();
10356                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10357                     width += buttons[k].el.getWidth()+15;
10358                 }else{
10359                     buttons[k].hide();
10360                 }
10361             }
10362         }
10363         return width;
10364     };
10365
10366     // private
10367     var handleEsc = function(d, k, e){
10368         if(opt && opt.closable !== false){
10369             dlg.hide();
10370         }
10371         if(e){
10372             e.stopEvent();
10373         }
10374     };
10375
10376     return {
10377         /**
10378          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10379          * @return {Roo.BasicDialog} The BasicDialog element
10380          */
10381         getDialog : function(){
10382            if(!dlg){
10383                 dlg = new Roo.BasicDialog("x-msg-box", {
10384                     autoCreate : true,
10385                     shadow: true,
10386                     draggable: true,
10387                     resizable:false,
10388                     constraintoviewport:false,
10389                     fixedcenter:true,
10390                     collapsible : false,
10391                     shim:true,
10392                     modal: true,
10393                     width:400, height:100,
10394                     buttonAlign:"center",
10395                     closeClick : function(){
10396                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10397                             handleButton("no");
10398                         }else{
10399                             handleButton("cancel");
10400                         }
10401                     }
10402                 });
10403                 dlg.on("hide", handleHide);
10404                 mask = dlg.mask;
10405                 dlg.addKeyListener(27, handleEsc);
10406                 buttons = {};
10407                 var bt = this.buttonText;
10408                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10409                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10410                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10411                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10412                 bodyEl = dlg.body.createChild({
10413
10414                     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>'
10415                 });
10416                 msgEl = bodyEl.dom.firstChild;
10417                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10418                 textboxEl.enableDisplayMode();
10419                 textboxEl.addKeyListener([10,13], function(){
10420                     if(dlg.isVisible() && opt && opt.buttons){
10421                         if(opt.buttons.ok){
10422                             handleButton("ok");
10423                         }else if(opt.buttons.yes){
10424                             handleButton("yes");
10425                         }
10426                     }
10427                 });
10428                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10429                 textareaEl.enableDisplayMode();
10430                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10431                 progressEl.enableDisplayMode();
10432                 var pf = progressEl.dom.firstChild;
10433                 if (pf) {
10434                     pp = Roo.get(pf.firstChild);
10435                     pp.setHeight(pf.offsetHeight);
10436                 }
10437                 
10438             }
10439             return dlg;
10440         },
10441
10442         /**
10443          * Updates the message box body text
10444          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10445          * the XHTML-compliant non-breaking space character '&amp;#160;')
10446          * @return {Roo.MessageBox} This message box
10447          */
10448         updateText : function(text){
10449             if(!dlg.isVisible() && !opt.width){
10450                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10451             }
10452             msgEl.innerHTML = text || '&#160;';
10453       
10454             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10455             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10456             var w = Math.max(
10457                     Math.min(opt.width || cw , this.maxWidth), 
10458                     Math.max(opt.minWidth || this.minWidth, bwidth)
10459             );
10460             if(opt.prompt){
10461                 activeTextEl.setWidth(w);
10462             }
10463             if(dlg.isVisible()){
10464                 dlg.fixedcenter = false;
10465             }
10466             // to big, make it scroll. = But as usual stupid IE does not support
10467             // !important..
10468             
10469             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10470                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10471                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10472             } else {
10473                 bodyEl.dom.style.height = '';
10474                 bodyEl.dom.style.overflowY = '';
10475             }
10476             if (cw > w) {
10477                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10478             } else {
10479                 bodyEl.dom.style.overflowX = '';
10480             }
10481             
10482             dlg.setContentSize(w, bodyEl.getHeight());
10483             if(dlg.isVisible()){
10484                 dlg.fixedcenter = true;
10485             }
10486             return this;
10487         },
10488
10489         /**
10490          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10491          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10492          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10493          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10494          * @return {Roo.MessageBox} This message box
10495          */
10496         updateProgress : function(value, text){
10497             if(text){
10498                 this.updateText(text);
10499             }
10500             if (pp) { // weird bug on my firefox - for some reason this is not defined
10501                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10502             }
10503             return this;
10504         },        
10505
10506         /**
10507          * Returns true if the message box is currently displayed
10508          * @return {Boolean} True if the message box is visible, else false
10509          */
10510         isVisible : function(){
10511             return dlg && dlg.isVisible();  
10512         },
10513
10514         /**
10515          * Hides the message box if it is displayed
10516          */
10517         hide : function(){
10518             if(this.isVisible()){
10519                 dlg.hide();
10520             }  
10521         },
10522
10523         /**
10524          * Displays a new message box, or reinitializes an existing message box, based on the config options
10525          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10526          * The following config object properties are supported:
10527          * <pre>
10528 Property    Type             Description
10529 ----------  ---------------  ------------------------------------------------------------------------------------
10530 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10531                                    closes (defaults to undefined)
10532 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10533                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10534 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10535                                    progress and wait dialogs will ignore this property and always hide the
10536                                    close button as they can only be closed programmatically.
10537 cls               String           A custom CSS class to apply to the message box element
10538 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10539                                    displayed (defaults to 75)
10540 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10541                                    function will be btn (the name of the button that was clicked, if applicable,
10542                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10543                                    Progress and wait dialogs will ignore this option since they do not respond to
10544                                    user actions and can only be closed programmatically, so any required function
10545                                    should be called by the same code after it closes the dialog.
10546 icon              String           A CSS class that provides a background image to be used as an icon for
10547                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10548 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10549 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10550 modal             Boolean          False to allow user interaction with the page while the message box is
10551                                    displayed (defaults to true)
10552 msg               String           A string that will replace the existing message box body text (defaults
10553                                    to the XHTML-compliant non-breaking space character '&#160;')
10554 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10555 progress          Boolean          True to display a progress bar (defaults to false)
10556 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10557 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10558 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10559 title             String           The title text
10560 value             String           The string value to set into the active textbox element if displayed
10561 wait              Boolean          True to display a progress bar (defaults to false)
10562 width             Number           The width of the dialog in pixels
10563 </pre>
10564          *
10565          * Example usage:
10566          * <pre><code>
10567 Roo.Msg.show({
10568    title: 'Address',
10569    msg: 'Please enter your address:',
10570    width: 300,
10571    buttons: Roo.MessageBox.OKCANCEL,
10572    multiline: true,
10573    fn: saveAddress,
10574    animEl: 'addAddressBtn'
10575 });
10576 </code></pre>
10577          * @param {Object} config Configuration options
10578          * @return {Roo.MessageBox} This message box
10579          */
10580         show : function(options)
10581         {
10582             
10583             // this causes nightmares if you show one dialog after another
10584             // especially on callbacks..
10585              
10586             if(this.isVisible()){
10587                 
10588                 this.hide();
10589                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10590                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10591                 Roo.log("New Dialog Message:" +  options.msg )
10592                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10593                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10594                 
10595             }
10596             var d = this.getDialog();
10597             opt = options;
10598             d.setTitle(opt.title || "&#160;");
10599             d.close.setDisplayed(opt.closable !== false);
10600             activeTextEl = textboxEl;
10601             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10602             if(opt.prompt){
10603                 if(opt.multiline){
10604                     textboxEl.hide();
10605                     textareaEl.show();
10606                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10607                         opt.multiline : this.defaultTextHeight);
10608                     activeTextEl = textareaEl;
10609                 }else{
10610                     textboxEl.show();
10611                     textareaEl.hide();
10612                 }
10613             }else{
10614                 textboxEl.hide();
10615                 textareaEl.hide();
10616             }
10617             progressEl.setDisplayed(opt.progress === true);
10618             this.updateProgress(0);
10619             activeTextEl.dom.value = opt.value || "";
10620             if(opt.prompt){
10621                 dlg.setDefaultButton(activeTextEl);
10622             }else{
10623                 var bs = opt.buttons;
10624                 var db = null;
10625                 if(bs && bs.ok){
10626                     db = buttons["ok"];
10627                 }else if(bs && bs.yes){
10628                     db = buttons["yes"];
10629                 }
10630                 dlg.setDefaultButton(db);
10631             }
10632             bwidth = updateButtons(opt.buttons);
10633             this.updateText(opt.msg);
10634             if(opt.cls){
10635                 d.el.addClass(opt.cls);
10636             }
10637             d.proxyDrag = opt.proxyDrag === true;
10638             d.modal = opt.modal !== false;
10639             d.mask = opt.modal !== false ? mask : false;
10640             if(!d.isVisible()){
10641                 // force it to the end of the z-index stack so it gets a cursor in FF
10642                 document.body.appendChild(dlg.el.dom);
10643                 d.animateTarget = null;
10644                 d.show(options.animEl);
10645             }
10646             return this;
10647         },
10648
10649         /**
10650          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10651          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10652          * and closing the message box when the process is complete.
10653          * @param {String} title The title bar text
10654          * @param {String} msg The message box body text
10655          * @return {Roo.MessageBox} This message box
10656          */
10657         progress : function(title, msg){
10658             this.show({
10659                 title : title,
10660                 msg : msg,
10661                 buttons: false,
10662                 progress:true,
10663                 closable:false,
10664                 minWidth: this.minProgressWidth,
10665                 modal : true
10666             });
10667             return this;
10668         },
10669
10670         /**
10671          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10672          * If a callback function is passed it will be called after the user clicks the button, and the
10673          * id of the button that was clicked will be passed as the only parameter to the callback
10674          * (could also be the top-right close button).
10675          * @param {String} title The title bar text
10676          * @param {String} msg The message box body text
10677          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10678          * @param {Object} scope (optional) The scope of the callback function
10679          * @return {Roo.MessageBox} This message box
10680          */
10681         alert : function(title, msg, fn, scope){
10682             this.show({
10683                 title : title,
10684                 msg : msg,
10685                 buttons: this.OK,
10686                 fn: fn,
10687                 scope : scope,
10688                 modal : true
10689             });
10690             return this;
10691         },
10692
10693         /**
10694          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10695          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10696          * You are responsible for closing the message box when the process is complete.
10697          * @param {String} msg The message box body text
10698          * @param {String} title (optional) The title bar text
10699          * @return {Roo.MessageBox} This message box
10700          */
10701         wait : function(msg, title){
10702             this.show({
10703                 title : title,
10704                 msg : msg,
10705                 buttons: false,
10706                 closable:false,
10707                 progress:true,
10708                 modal:true,
10709                 width:300,
10710                 wait:true
10711             });
10712             waitTimer = Roo.TaskMgr.start({
10713                 run: function(i){
10714                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10715                 },
10716                 interval: 1000
10717             });
10718             return this;
10719         },
10720
10721         /**
10722          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10723          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10724          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10725          * @param {String} title The title bar text
10726          * @param {String} msg The message box body text
10727          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10728          * @param {Object} scope (optional) The scope of the callback function
10729          * @return {Roo.MessageBox} This message box
10730          */
10731         confirm : function(title, msg, fn, scope){
10732             this.show({
10733                 title : title,
10734                 msg : msg,
10735                 buttons: this.YESNO,
10736                 fn: fn,
10737                 scope : scope,
10738                 modal : true
10739             });
10740             return this;
10741         },
10742
10743         /**
10744          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10745          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10746          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10747          * (could also be the top-right close button) and the text that was entered will be passed as the two
10748          * parameters to the callback.
10749          * @param {String} title The title bar text
10750          * @param {String} msg The message box body text
10751          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10752          * @param {Object} scope (optional) The scope of the callback function
10753          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10754          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10755          * @return {Roo.MessageBox} This message box
10756          */
10757         prompt : function(title, msg, fn, scope, multiline){
10758             this.show({
10759                 title : title,
10760                 msg : msg,
10761                 buttons: this.OKCANCEL,
10762                 fn: fn,
10763                 minWidth:250,
10764                 scope : scope,
10765                 prompt:true,
10766                 multiline: multiline,
10767                 modal : true
10768             });
10769             return this;
10770         },
10771
10772         /**
10773          * Button config that displays a single OK button
10774          * @type Object
10775          */
10776         OK : {ok:true},
10777         /**
10778          * Button config that displays Yes and No buttons
10779          * @type Object
10780          */
10781         YESNO : {yes:true, no:true},
10782         /**
10783          * Button config that displays OK and Cancel buttons
10784          * @type Object
10785          */
10786         OKCANCEL : {ok:true, cancel:true},
10787         /**
10788          * Button config that displays Yes, No and Cancel buttons
10789          * @type Object
10790          */
10791         YESNOCANCEL : {yes:true, no:true, cancel:true},
10792
10793         /**
10794          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10795          * @type Number
10796          */
10797         defaultTextHeight : 75,
10798         /**
10799          * The maximum width in pixels of the message box (defaults to 600)
10800          * @type Number
10801          */
10802         maxWidth : 600,
10803         /**
10804          * The minimum width in pixels of the message box (defaults to 100)
10805          * @type Number
10806          */
10807         minWidth : 100,
10808         /**
10809          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10810          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10811          * @type Number
10812          */
10813         minProgressWidth : 250,
10814         /**
10815          * An object containing the default button text strings that can be overriden for localized language support.
10816          * Supported properties are: ok, cancel, yes and no.
10817          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10818          * @type Object
10819          */
10820         buttonText : {
10821             ok : "OK",
10822             cancel : "Cancel",
10823             yes : "Yes",
10824             no : "No"
10825         }
10826     };
10827 }();
10828
10829 /**
10830  * Shorthand for {@link Roo.MessageBox}
10831  */
10832 Roo.Msg = Roo.MessageBox;/*
10833  * Based on:
10834  * Ext JS Library 1.1.1
10835  * Copyright(c) 2006-2007, Ext JS, LLC.
10836  *
10837  * Originally Released Under LGPL - original licence link has changed is not relivant.
10838  *
10839  * Fork - LGPL
10840  * <script type="text/javascript">
10841  */
10842 /**
10843  * @class Roo.QuickTips
10844  * Provides attractive and customizable tooltips for any element.
10845  * @static
10846  */
10847 Roo.QuickTips = function(){
10848     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10849     var ce, bd, xy, dd;
10850     var visible = false, disabled = true, inited = false;
10851     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10852     
10853     var onOver = function(e){
10854         if(disabled){
10855             return;
10856         }
10857         var t = e.getTarget();
10858         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10859             return;
10860         }
10861         if(ce && t == ce.el){
10862             clearTimeout(hideProc);
10863             return;
10864         }
10865         if(t && tagEls[t.id]){
10866             tagEls[t.id].el = t;
10867             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10868             return;
10869         }
10870         var ttp, et = Roo.fly(t);
10871         var ns = cfg.namespace;
10872         if(tm.interceptTitles && t.title){
10873             ttp = t.title;
10874             t.qtip = ttp;
10875             t.removeAttribute("title");
10876             e.preventDefault();
10877         }else{
10878             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10879         }
10880         if(ttp){
10881             showProc = show.defer(tm.showDelay, tm, [{
10882                 el: t, 
10883                 text: ttp.replace(/\\n/g,'<br/>'),
10884                 width: et.getAttributeNS(ns, cfg.width),
10885                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10886                 title: et.getAttributeNS(ns, cfg.title),
10887                     cls: et.getAttributeNS(ns, cfg.cls)
10888             }]);
10889         }
10890     };
10891     
10892     var onOut = function(e){
10893         clearTimeout(showProc);
10894         var t = e.getTarget();
10895         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10896             hideProc = setTimeout(hide, tm.hideDelay);
10897         }
10898     };
10899     
10900     var onMove = function(e){
10901         if(disabled){
10902             return;
10903         }
10904         xy = e.getXY();
10905         xy[1] += 18;
10906         if(tm.trackMouse && ce){
10907             el.setXY(xy);
10908         }
10909     };
10910     
10911     var onDown = function(e){
10912         clearTimeout(showProc);
10913         clearTimeout(hideProc);
10914         if(!e.within(el)){
10915             if(tm.hideOnClick){
10916                 hide();
10917                 tm.disable();
10918                 tm.enable.defer(100, tm);
10919             }
10920         }
10921     };
10922     
10923     var getPad = function(){
10924         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10925     };
10926
10927     var show = function(o){
10928         if(disabled){
10929             return;
10930         }
10931         clearTimeout(dismissProc);
10932         ce = o;
10933         if(removeCls){ // in case manually hidden
10934             el.removeClass(removeCls);
10935             removeCls = null;
10936         }
10937         if(ce.cls){
10938             el.addClass(ce.cls);
10939             removeCls = ce.cls;
10940         }
10941         if(ce.title){
10942             tipTitle.update(ce.title);
10943             tipTitle.show();
10944         }else{
10945             tipTitle.update('');
10946             tipTitle.hide();
10947         }
10948         el.dom.style.width  = tm.maxWidth+'px';
10949         //tipBody.dom.style.width = '';
10950         tipBodyText.update(o.text);
10951         var p = getPad(), w = ce.width;
10952         if(!w){
10953             var td = tipBodyText.dom;
10954             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10955             if(aw > tm.maxWidth){
10956                 w = tm.maxWidth;
10957             }else if(aw < tm.minWidth){
10958                 w = tm.minWidth;
10959             }else{
10960                 w = aw;
10961             }
10962         }
10963         //tipBody.setWidth(w);
10964         el.setWidth(parseInt(w, 10) + p);
10965         if(ce.autoHide === false){
10966             close.setDisplayed(true);
10967             if(dd){
10968                 dd.unlock();
10969             }
10970         }else{
10971             close.setDisplayed(false);
10972             if(dd){
10973                 dd.lock();
10974             }
10975         }
10976         if(xy){
10977             el.avoidY = xy[1]-18;
10978             el.setXY(xy);
10979         }
10980         if(tm.animate){
10981             el.setOpacity(.1);
10982             el.setStyle("visibility", "visible");
10983             el.fadeIn({callback: afterShow});
10984         }else{
10985             afterShow();
10986         }
10987     };
10988     
10989     var afterShow = function(){
10990         if(ce){
10991             el.show();
10992             esc.enable();
10993             if(tm.autoDismiss && ce.autoHide !== false){
10994                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10995             }
10996         }
10997     };
10998     
10999     var hide = function(noanim){
11000         clearTimeout(dismissProc);
11001         clearTimeout(hideProc);
11002         ce = null;
11003         if(el.isVisible()){
11004             esc.disable();
11005             if(noanim !== true && tm.animate){
11006                 el.fadeOut({callback: afterHide});
11007             }else{
11008                 afterHide();
11009             } 
11010         }
11011     };
11012     
11013     var afterHide = function(){
11014         el.hide();
11015         if(removeCls){
11016             el.removeClass(removeCls);
11017             removeCls = null;
11018         }
11019     };
11020     
11021     return {
11022         /**
11023         * @cfg {Number} minWidth
11024         * The minimum width of the quick tip (defaults to 40)
11025         */
11026        minWidth : 40,
11027         /**
11028         * @cfg {Number} maxWidth
11029         * The maximum width of the quick tip (defaults to 300)
11030         */
11031        maxWidth : 300,
11032         /**
11033         * @cfg {Boolean} interceptTitles
11034         * True to automatically use the element's DOM title value if available (defaults to false)
11035         */
11036        interceptTitles : false,
11037         /**
11038         * @cfg {Boolean} trackMouse
11039         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11040         */
11041        trackMouse : false,
11042         /**
11043         * @cfg {Boolean} hideOnClick
11044         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11045         */
11046        hideOnClick : true,
11047         /**
11048         * @cfg {Number} showDelay
11049         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11050         */
11051        showDelay : 500,
11052         /**
11053         * @cfg {Number} hideDelay
11054         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11055         */
11056        hideDelay : 200,
11057         /**
11058         * @cfg {Boolean} autoHide
11059         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11060         * Used in conjunction with hideDelay.
11061         */
11062        autoHide : true,
11063         /**
11064         * @cfg {Boolean}
11065         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11066         * (defaults to true).  Used in conjunction with autoDismissDelay.
11067         */
11068        autoDismiss : true,
11069         /**
11070         * @cfg {Number}
11071         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11072         */
11073        autoDismissDelay : 5000,
11074        /**
11075         * @cfg {Boolean} animate
11076         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11077         */
11078        animate : false,
11079
11080        /**
11081         * @cfg {String} title
11082         * Title text to display (defaults to '').  This can be any valid HTML markup.
11083         */
11084         title: '',
11085        /**
11086         * @cfg {String} text
11087         * Body text to display (defaults to '').  This can be any valid HTML markup.
11088         */
11089         text : '',
11090        /**
11091         * @cfg {String} cls
11092         * A CSS class to apply to the base quick tip element (defaults to '').
11093         */
11094         cls : '',
11095        /**
11096         * @cfg {Number} width
11097         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11098         * minWidth or maxWidth.
11099         */
11100         width : null,
11101
11102     /**
11103      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11104      * or display QuickTips in a page.
11105      */
11106        init : function(){
11107           tm = Roo.QuickTips;
11108           cfg = tm.tagConfig;
11109           if(!inited){
11110               if(!Roo.isReady){ // allow calling of init() before onReady
11111                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11112                   return;
11113               }
11114               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11115               el.fxDefaults = {stopFx: true};
11116               // maximum custom styling
11117               //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>');
11118               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>');              
11119               tipTitle = el.child('h3');
11120               tipTitle.enableDisplayMode("block");
11121               tipBody = el.child('div.x-tip-bd');
11122               tipBodyText = el.child('div.x-tip-bd-inner');
11123               //bdLeft = el.child('div.x-tip-bd-left');
11124               //bdRight = el.child('div.x-tip-bd-right');
11125               close = el.child('div.x-tip-close');
11126               close.enableDisplayMode("block");
11127               close.on("click", hide);
11128               var d = Roo.get(document);
11129               d.on("mousedown", onDown);
11130               d.on("mouseover", onOver);
11131               d.on("mouseout", onOut);
11132               d.on("mousemove", onMove);
11133               esc = d.addKeyListener(27, hide);
11134               esc.disable();
11135               if(Roo.dd.DD){
11136                   dd = el.initDD("default", null, {
11137                       onDrag : function(){
11138                           el.sync();  
11139                       }
11140                   });
11141                   dd.setHandleElId(tipTitle.id);
11142                   dd.lock();
11143               }
11144               inited = true;
11145           }
11146           this.enable(); 
11147        },
11148
11149     /**
11150      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11151      * are supported:
11152      * <pre>
11153 Property    Type                   Description
11154 ----------  ---------------------  ------------------------------------------------------------------------
11155 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11156      * </ul>
11157      * @param {Object} config The config object
11158      */
11159        register : function(config){
11160            var cs = config instanceof Array ? config : arguments;
11161            for(var i = 0, len = cs.length; i < len; i++) {
11162                var c = cs[i];
11163                var target = c.target;
11164                if(target){
11165                    if(target instanceof Array){
11166                        for(var j = 0, jlen = target.length; j < jlen; j++){
11167                            tagEls[target[j]] = c;
11168                        }
11169                    }else{
11170                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11171                    }
11172                }
11173            }
11174        },
11175
11176     /**
11177      * Removes this quick tip from its element and destroys it.
11178      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11179      */
11180        unregister : function(el){
11181            delete tagEls[Roo.id(el)];
11182        },
11183
11184     /**
11185      * Enable this quick tip.
11186      */
11187        enable : function(){
11188            if(inited && disabled){
11189                locks.pop();
11190                if(locks.length < 1){
11191                    disabled = false;
11192                }
11193            }
11194        },
11195
11196     /**
11197      * Disable this quick tip.
11198      */
11199        disable : function(){
11200           disabled = true;
11201           clearTimeout(showProc);
11202           clearTimeout(hideProc);
11203           clearTimeout(dismissProc);
11204           if(ce){
11205               hide(true);
11206           }
11207           locks.push(1);
11208        },
11209
11210     /**
11211      * Returns true if the quick tip is enabled, else false.
11212      */
11213        isEnabled : function(){
11214             return !disabled;
11215        },
11216
11217         // private
11218        tagConfig : {
11219            namespace : "roo", // was ext?? this may break..
11220            alt_namespace : "ext",
11221            attribute : "qtip",
11222            width : "width",
11223            target : "target",
11224            title : "qtitle",
11225            hide : "hide",
11226            cls : "qclass"
11227        }
11228    };
11229 }();
11230
11231 // backwards compat
11232 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11233  * Based on:
11234  * Ext JS Library 1.1.1
11235  * Copyright(c) 2006-2007, Ext JS, LLC.
11236  *
11237  * Originally Released Under LGPL - original licence link has changed is not relivant.
11238  *
11239  * Fork - LGPL
11240  * <script type="text/javascript">
11241  */
11242  
11243
11244 /**
11245  * @class Roo.tree.TreePanel
11246  * @extends Roo.data.Tree
11247  * @cfg {Roo.tree.TreeNode} root The root node
11248  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11249  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11250  * @cfg {Boolean} enableDD true to enable drag and drop
11251  * @cfg {Boolean} enableDrag true to enable just drag
11252  * @cfg {Boolean} enableDrop true to enable just drop
11253  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11254  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11255  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11256  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11257  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11258  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11259  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11260  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11261  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11262  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11263  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11264  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11265  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11266  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11267  * @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>
11268  * @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>
11269  * 
11270  * @constructor
11271  * @param {String/HTMLElement/Element} el The container element
11272  * @param {Object} config
11273  */
11274 Roo.tree.TreePanel = function(el, config){
11275     var root = false;
11276     var loader = false;
11277     if (config.root) {
11278         root = config.root;
11279         delete config.root;
11280     }
11281     if (config.loader) {
11282         loader = config.loader;
11283         delete config.loader;
11284     }
11285     
11286     Roo.apply(this, config);
11287     Roo.tree.TreePanel.superclass.constructor.call(this);
11288     this.el = Roo.get(el);
11289     this.el.addClass('x-tree');
11290     //console.log(root);
11291     if (root) {
11292         this.setRootNode( Roo.factory(root, Roo.tree));
11293     }
11294     if (loader) {
11295         this.loader = Roo.factory(loader, Roo.tree);
11296     }
11297    /**
11298     * Read-only. The id of the container element becomes this TreePanel's id.
11299     */
11300     this.id = this.el.id;
11301     this.addEvents({
11302         /**
11303         * @event beforeload
11304         * Fires before a node is loaded, return false to cancel
11305         * @param {Node} node The node being loaded
11306         */
11307         "beforeload" : true,
11308         /**
11309         * @event load
11310         * Fires when a node is loaded
11311         * @param {Node} node The node that was loaded
11312         */
11313         "load" : true,
11314         /**
11315         * @event textchange
11316         * Fires when the text for a node is changed
11317         * @param {Node} node The node
11318         * @param {String} text The new text
11319         * @param {String} oldText The old text
11320         */
11321         "textchange" : true,
11322         /**
11323         * @event beforeexpand
11324         * Fires before a node is expanded, return false to cancel.
11325         * @param {Node} node The node
11326         * @param {Boolean} deep
11327         * @param {Boolean} anim
11328         */
11329         "beforeexpand" : true,
11330         /**
11331         * @event beforecollapse
11332         * Fires before a node is collapsed, return false to cancel.
11333         * @param {Node} node The node
11334         * @param {Boolean} deep
11335         * @param {Boolean} anim
11336         */
11337         "beforecollapse" : true,
11338         /**
11339         * @event expand
11340         * Fires when a node is expanded
11341         * @param {Node} node The node
11342         */
11343         "expand" : true,
11344         /**
11345         * @event disabledchange
11346         * Fires when the disabled status of a node changes
11347         * @param {Node} node The node
11348         * @param {Boolean} disabled
11349         */
11350         "disabledchange" : true,
11351         /**
11352         * @event collapse
11353         * Fires when a node is collapsed
11354         * @param {Node} node The node
11355         */
11356         "collapse" : true,
11357         /**
11358         * @event beforeclick
11359         * Fires before click processing on a node. Return false to cancel the default action.
11360         * @param {Node} node The node
11361         * @param {Roo.EventObject} e The event object
11362         */
11363         "beforeclick":true,
11364         /**
11365         * @event checkchange
11366         * Fires when a node with a checkbox's checked property changes
11367         * @param {Node} this This node
11368         * @param {Boolean} checked
11369         */
11370         "checkchange":true,
11371         /**
11372         * @event click
11373         * Fires when a node is clicked
11374         * @param {Node} node The node
11375         * @param {Roo.EventObject} e The event object
11376         */
11377         "click":true,
11378         /**
11379         * @event dblclick
11380         * Fires when a node is double clicked
11381         * @param {Node} node The node
11382         * @param {Roo.EventObject} e The event object
11383         */
11384         "dblclick":true,
11385         /**
11386         * @event contextmenu
11387         * Fires when a node is right clicked
11388         * @param {Node} node The node
11389         * @param {Roo.EventObject} e The event object
11390         */
11391         "contextmenu":true,
11392         /**
11393         * @event beforechildrenrendered
11394         * Fires right before the child nodes for a node are rendered
11395         * @param {Node} node The node
11396         */
11397         "beforechildrenrendered":true,
11398         /**
11399         * @event startdrag
11400         * Fires when a node starts being dragged
11401         * @param {Roo.tree.TreePanel} this
11402         * @param {Roo.tree.TreeNode} node
11403         * @param {event} e The raw browser event
11404         */ 
11405        "startdrag" : true,
11406        /**
11407         * @event enddrag
11408         * Fires when a drag operation is complete
11409         * @param {Roo.tree.TreePanel} this
11410         * @param {Roo.tree.TreeNode} node
11411         * @param {event} e The raw browser event
11412         */
11413        "enddrag" : true,
11414        /**
11415         * @event dragdrop
11416         * Fires when a dragged node is dropped on a valid DD target
11417         * @param {Roo.tree.TreePanel} this
11418         * @param {Roo.tree.TreeNode} node
11419         * @param {DD} dd The dd it was dropped on
11420         * @param {event} e The raw browser event
11421         */
11422        "dragdrop" : true,
11423        /**
11424         * @event beforenodedrop
11425         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11426         * passed to handlers has the following properties:<br />
11427         * <ul style="padding:5px;padding-left:16px;">
11428         * <li>tree - The TreePanel</li>
11429         * <li>target - The node being targeted for the drop</li>
11430         * <li>data - The drag data from the drag source</li>
11431         * <li>point - The point of the drop - append, above or below</li>
11432         * <li>source - The drag source</li>
11433         * <li>rawEvent - Raw mouse event</li>
11434         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11435         * to be inserted by setting them on this object.</li>
11436         * <li>cancel - Set this to true to cancel the drop.</li>
11437         * </ul>
11438         * @param {Object} dropEvent
11439         */
11440        "beforenodedrop" : true,
11441        /**
11442         * @event nodedrop
11443         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11444         * passed to handlers has the following properties:<br />
11445         * <ul style="padding:5px;padding-left:16px;">
11446         * <li>tree - The TreePanel</li>
11447         * <li>target - The node being targeted for the drop</li>
11448         * <li>data - The drag data from the drag source</li>
11449         * <li>point - The point of the drop - append, above or below</li>
11450         * <li>source - The drag source</li>
11451         * <li>rawEvent - Raw mouse event</li>
11452         * <li>dropNode - Dropped node(s).</li>
11453         * </ul>
11454         * @param {Object} dropEvent
11455         */
11456        "nodedrop" : true,
11457         /**
11458         * @event nodedragover
11459         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11460         * passed to handlers has the following properties:<br />
11461         * <ul style="padding:5px;padding-left:16px;">
11462         * <li>tree - The TreePanel</li>
11463         * <li>target - The node being targeted for the drop</li>
11464         * <li>data - The drag data from the drag source</li>
11465         * <li>point - The point of the drop - append, above or below</li>
11466         * <li>source - The drag source</li>
11467         * <li>rawEvent - Raw mouse event</li>
11468         * <li>dropNode - Drop node(s) provided by the source.</li>
11469         * <li>cancel - Set this to true to signal drop not allowed.</li>
11470         * </ul>
11471         * @param {Object} dragOverEvent
11472         */
11473        "nodedragover" : true,
11474        /**
11475         * @event appendnode
11476         * Fires when append node to the tree
11477         * @param {Roo.tree.TreePanel} this
11478         * @param {Roo.tree.TreeNode} node
11479         * @param {Number} index The index of the newly appended node
11480         */
11481        "appendnode" : true
11482         
11483     });
11484     if(this.singleExpand){
11485        this.on("beforeexpand", this.restrictExpand, this);
11486     }
11487     if (this.editor) {
11488         this.editor.tree = this;
11489         this.editor = Roo.factory(this.editor, Roo.tree);
11490     }
11491     
11492     if (this.selModel) {
11493         this.selModel = Roo.factory(this.selModel, Roo.tree);
11494     }
11495    
11496 };
11497 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11498     rootVisible : true,
11499     animate: Roo.enableFx,
11500     lines : true,
11501     enableDD : false,
11502     hlDrop : Roo.enableFx,
11503   
11504     renderer: false,
11505     
11506     rendererTip: false,
11507     // private
11508     restrictExpand : function(node){
11509         var p = node.parentNode;
11510         if(p){
11511             if(p.expandedChild && p.expandedChild.parentNode == p){
11512                 p.expandedChild.collapse();
11513             }
11514             p.expandedChild = node;
11515         }
11516     },
11517
11518     // private override
11519     setRootNode : function(node){
11520         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11521         if(!this.rootVisible){
11522             node.ui = new Roo.tree.RootTreeNodeUI(node);
11523         }
11524         return node;
11525     },
11526
11527     /**
11528      * Returns the container element for this TreePanel
11529      */
11530     getEl : function(){
11531         return this.el;
11532     },
11533
11534     /**
11535      * Returns the default TreeLoader for this TreePanel
11536      */
11537     getLoader : function(){
11538         return this.loader;
11539     },
11540
11541     /**
11542      * Expand all nodes
11543      */
11544     expandAll : function(){
11545         this.root.expand(true);
11546     },
11547
11548     /**
11549      * Collapse all nodes
11550      */
11551     collapseAll : function(){
11552         this.root.collapse(true);
11553     },
11554
11555     /**
11556      * Returns the selection model used by this TreePanel
11557      */
11558     getSelectionModel : function(){
11559         if(!this.selModel){
11560             this.selModel = new Roo.tree.DefaultSelectionModel();
11561         }
11562         return this.selModel;
11563     },
11564
11565     /**
11566      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11567      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11568      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11569      * @return {Array}
11570      */
11571     getChecked : function(a, startNode){
11572         startNode = startNode || this.root;
11573         var r = [];
11574         var f = function(){
11575             if(this.attributes.checked){
11576                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11577             }
11578         }
11579         startNode.cascade(f);
11580         return r;
11581     },
11582
11583     /**
11584      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11585      * @param {String} path
11586      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11587      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11588      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11589      */
11590     expandPath : function(path, attr, callback){
11591         attr = attr || "id";
11592         var keys = path.split(this.pathSeparator);
11593         var curNode = this.root;
11594         if(curNode.attributes[attr] != keys[1]){ // invalid root
11595             if(callback){
11596                 callback(false, null);
11597             }
11598             return;
11599         }
11600         var index = 1;
11601         var f = function(){
11602             if(++index == keys.length){
11603                 if(callback){
11604                     callback(true, curNode);
11605                 }
11606                 return;
11607             }
11608             var c = curNode.findChild(attr, keys[index]);
11609             if(!c){
11610                 if(callback){
11611                     callback(false, curNode);
11612                 }
11613                 return;
11614             }
11615             curNode = c;
11616             c.expand(false, false, f);
11617         };
11618         curNode.expand(false, false, f);
11619     },
11620
11621     /**
11622      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11623      * @param {String} path
11624      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11625      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11626      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11627      */
11628     selectPath : function(path, attr, callback){
11629         attr = attr || "id";
11630         var keys = path.split(this.pathSeparator);
11631         var v = keys.pop();
11632         if(keys.length > 0){
11633             var f = function(success, node){
11634                 if(success && node){
11635                     var n = node.findChild(attr, v);
11636                     if(n){
11637                         n.select();
11638                         if(callback){
11639                             callback(true, n);
11640                         }
11641                     }else if(callback){
11642                         callback(false, n);
11643                     }
11644                 }else{
11645                     if(callback){
11646                         callback(false, n);
11647                     }
11648                 }
11649             };
11650             this.expandPath(keys.join(this.pathSeparator), attr, f);
11651         }else{
11652             this.root.select();
11653             if(callback){
11654                 callback(true, this.root);
11655             }
11656         }
11657     },
11658
11659     getTreeEl : function(){
11660         return this.el;
11661     },
11662
11663     /**
11664      * Trigger rendering of this TreePanel
11665      */
11666     render : function(){
11667         if (this.innerCt) {
11668             return this; // stop it rendering more than once!!
11669         }
11670         
11671         this.innerCt = this.el.createChild({tag:"ul",
11672                cls:"x-tree-root-ct " +
11673                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11674
11675         if(this.containerScroll){
11676             Roo.dd.ScrollManager.register(this.el);
11677         }
11678         if((this.enableDD || this.enableDrop) && !this.dropZone){
11679            /**
11680             * The dropZone used by this tree if drop is enabled
11681             * @type Roo.tree.TreeDropZone
11682             */
11683              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11684                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11685            });
11686         }
11687         if((this.enableDD || this.enableDrag) && !this.dragZone){
11688            /**
11689             * The dragZone used by this tree if drag is enabled
11690             * @type Roo.tree.TreeDragZone
11691             */
11692             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11693                ddGroup: this.ddGroup || "TreeDD",
11694                scroll: this.ddScroll
11695            });
11696         }
11697         this.getSelectionModel().init(this);
11698         if (!this.root) {
11699             Roo.log("ROOT not set in tree");
11700             return this;
11701         }
11702         this.root.render();
11703         if(!this.rootVisible){
11704             this.root.renderChildren();
11705         }
11706         return this;
11707     }
11708 });/*
11709  * Based on:
11710  * Ext JS Library 1.1.1
11711  * Copyright(c) 2006-2007, Ext JS, LLC.
11712  *
11713  * Originally Released Under LGPL - original licence link has changed is not relivant.
11714  *
11715  * Fork - LGPL
11716  * <script type="text/javascript">
11717  */
11718  
11719
11720 /**
11721  * @class Roo.tree.DefaultSelectionModel
11722  * @extends Roo.util.Observable
11723  * The default single selection for a TreePanel.
11724  * @param {Object} cfg Configuration
11725  */
11726 Roo.tree.DefaultSelectionModel = function(cfg){
11727    this.selNode = null;
11728    
11729    
11730    
11731    this.addEvents({
11732        /**
11733         * @event selectionchange
11734         * Fires when the selected node changes
11735         * @param {DefaultSelectionModel} this
11736         * @param {TreeNode} node the new selection
11737         */
11738        "selectionchange" : true,
11739
11740        /**
11741         * @event beforeselect
11742         * Fires before the selected node changes, return false to cancel the change
11743         * @param {DefaultSelectionModel} this
11744         * @param {TreeNode} node the new selection
11745         * @param {TreeNode} node the old selection
11746         */
11747        "beforeselect" : true
11748    });
11749    
11750     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11751 };
11752
11753 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11754     init : function(tree){
11755         this.tree = tree;
11756         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11757         tree.on("click", this.onNodeClick, this);
11758     },
11759     
11760     onNodeClick : function(node, e){
11761         if (e.ctrlKey && this.selNode == node)  {
11762             this.unselect(node);
11763             return;
11764         }
11765         this.select(node);
11766     },
11767     
11768     /**
11769      * Select a node.
11770      * @param {TreeNode} node The node to select
11771      * @return {TreeNode} The selected node
11772      */
11773     select : function(node){
11774         var last = this.selNode;
11775         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11776             if(last){
11777                 last.ui.onSelectedChange(false);
11778             }
11779             this.selNode = node;
11780             node.ui.onSelectedChange(true);
11781             this.fireEvent("selectionchange", this, node, last);
11782         }
11783         return node;
11784     },
11785     
11786     /**
11787      * Deselect a node.
11788      * @param {TreeNode} node The node to unselect
11789      */
11790     unselect : function(node){
11791         if(this.selNode == node){
11792             this.clearSelections();
11793         }    
11794     },
11795     
11796     /**
11797      * Clear all selections
11798      */
11799     clearSelections : function(){
11800         var n = this.selNode;
11801         if(n){
11802             n.ui.onSelectedChange(false);
11803             this.selNode = null;
11804             this.fireEvent("selectionchange", this, null);
11805         }
11806         return n;
11807     },
11808     
11809     /**
11810      * Get the selected node
11811      * @return {TreeNode} The selected node
11812      */
11813     getSelectedNode : function(){
11814         return this.selNode;    
11815     },
11816     
11817     /**
11818      * Returns true if the node is selected
11819      * @param {TreeNode} node The node to check
11820      * @return {Boolean}
11821      */
11822     isSelected : function(node){
11823         return this.selNode == node;  
11824     },
11825
11826     /**
11827      * Selects the node above the selected node in the tree, intelligently walking the nodes
11828      * @return TreeNode The new selection
11829      */
11830     selectPrevious : function(){
11831         var s = this.selNode || this.lastSelNode;
11832         if(!s){
11833             return null;
11834         }
11835         var ps = s.previousSibling;
11836         if(ps){
11837             if(!ps.isExpanded() || ps.childNodes.length < 1){
11838                 return this.select(ps);
11839             } else{
11840                 var lc = ps.lastChild;
11841                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11842                     lc = lc.lastChild;
11843                 }
11844                 return this.select(lc);
11845             }
11846         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11847             return this.select(s.parentNode);
11848         }
11849         return null;
11850     },
11851
11852     /**
11853      * Selects the node above the selected node in the tree, intelligently walking the nodes
11854      * @return TreeNode The new selection
11855      */
11856     selectNext : function(){
11857         var s = this.selNode || this.lastSelNode;
11858         if(!s){
11859             return null;
11860         }
11861         if(s.firstChild && s.isExpanded()){
11862              return this.select(s.firstChild);
11863          }else if(s.nextSibling){
11864              return this.select(s.nextSibling);
11865          }else if(s.parentNode){
11866             var newS = null;
11867             s.parentNode.bubble(function(){
11868                 if(this.nextSibling){
11869                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11870                     return false;
11871                 }
11872             });
11873             return newS;
11874          }
11875         return null;
11876     },
11877
11878     onKeyDown : function(e){
11879         var s = this.selNode || this.lastSelNode;
11880         // undesirable, but required
11881         var sm = this;
11882         if(!s){
11883             return;
11884         }
11885         var k = e.getKey();
11886         switch(k){
11887              case e.DOWN:
11888                  e.stopEvent();
11889                  this.selectNext();
11890              break;
11891              case e.UP:
11892                  e.stopEvent();
11893                  this.selectPrevious();
11894              break;
11895              case e.RIGHT:
11896                  e.preventDefault();
11897                  if(s.hasChildNodes()){
11898                      if(!s.isExpanded()){
11899                          s.expand();
11900                      }else if(s.firstChild){
11901                          this.select(s.firstChild, e);
11902                      }
11903                  }
11904              break;
11905              case e.LEFT:
11906                  e.preventDefault();
11907                  if(s.hasChildNodes() && s.isExpanded()){
11908                      s.collapse();
11909                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11910                      this.select(s.parentNode, e);
11911                  }
11912              break;
11913         };
11914     }
11915 });
11916
11917 /**
11918  * @class Roo.tree.MultiSelectionModel
11919  * @extends Roo.util.Observable
11920  * Multi selection for a TreePanel.
11921  * @param {Object} cfg Configuration
11922  */
11923 Roo.tree.MultiSelectionModel = function(){
11924    this.selNodes = [];
11925    this.selMap = {};
11926    this.addEvents({
11927        /**
11928         * @event selectionchange
11929         * Fires when the selected nodes change
11930         * @param {MultiSelectionModel} this
11931         * @param {Array} nodes Array of the selected nodes
11932         */
11933        "selectionchange" : true
11934    });
11935    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11936    
11937 };
11938
11939 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11940     init : function(tree){
11941         this.tree = tree;
11942         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11943         tree.on("click", this.onNodeClick, this);
11944     },
11945     
11946     onNodeClick : function(node, e){
11947         this.select(node, e, e.ctrlKey);
11948     },
11949     
11950     /**
11951      * Select a node.
11952      * @param {TreeNode} node The node to select
11953      * @param {EventObject} e (optional) An event associated with the selection
11954      * @param {Boolean} keepExisting True to retain existing selections
11955      * @return {TreeNode} The selected node
11956      */
11957     select : function(node, e, keepExisting){
11958         if(keepExisting !== true){
11959             this.clearSelections(true);
11960         }
11961         if(this.isSelected(node)){
11962             this.lastSelNode = node;
11963             return node;
11964         }
11965         this.selNodes.push(node);
11966         this.selMap[node.id] = node;
11967         this.lastSelNode = node;
11968         node.ui.onSelectedChange(true);
11969         this.fireEvent("selectionchange", this, this.selNodes);
11970         return node;
11971     },
11972     
11973     /**
11974      * Deselect a node.
11975      * @param {TreeNode} node The node to unselect
11976      */
11977     unselect : function(node){
11978         if(this.selMap[node.id]){
11979             node.ui.onSelectedChange(false);
11980             var sn = this.selNodes;
11981             var index = -1;
11982             if(sn.indexOf){
11983                 index = sn.indexOf(node);
11984             }else{
11985                 for(var i = 0, len = sn.length; i < len; i++){
11986                     if(sn[i] == node){
11987                         index = i;
11988                         break;
11989                     }
11990                 }
11991             }
11992             if(index != -1){
11993                 this.selNodes.splice(index, 1);
11994             }
11995             delete this.selMap[node.id];
11996             this.fireEvent("selectionchange", this, this.selNodes);
11997         }
11998     },
11999     
12000     /**
12001      * Clear all selections
12002      */
12003     clearSelections : function(suppressEvent){
12004         var sn = this.selNodes;
12005         if(sn.length > 0){
12006             for(var i = 0, len = sn.length; i < len; i++){
12007                 sn[i].ui.onSelectedChange(false);
12008             }
12009             this.selNodes = [];
12010             this.selMap = {};
12011             if(suppressEvent !== true){
12012                 this.fireEvent("selectionchange", this, this.selNodes);
12013             }
12014         }
12015     },
12016     
12017     /**
12018      * Returns true if the node is selected
12019      * @param {TreeNode} node The node to check
12020      * @return {Boolean}
12021      */
12022     isSelected : function(node){
12023         return this.selMap[node.id] ? true : false;  
12024     },
12025     
12026     /**
12027      * Returns an array of the selected nodes
12028      * @return {Array}
12029      */
12030     getSelectedNodes : function(){
12031         return this.selNodes;    
12032     },
12033
12034     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12035
12036     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12037
12038     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12039 });/*
12040  * Based on:
12041  * Ext JS Library 1.1.1
12042  * Copyright(c) 2006-2007, Ext JS, LLC.
12043  *
12044  * Originally Released Under LGPL - original licence link has changed is not relivant.
12045  *
12046  * Fork - LGPL
12047  * <script type="text/javascript">
12048  */
12049  
12050 /**
12051  * @class Roo.tree.TreeNode
12052  * @extends Roo.data.Node
12053  * @cfg {String} text The text for this node
12054  * @cfg {Boolean} expanded true to start the node expanded
12055  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12056  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12057  * @cfg {Boolean} disabled true to start the node disabled
12058  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12059  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12060  * @cfg {String} cls A css class to be added to the node
12061  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12062  * @cfg {String} href URL of the link used for the node (defaults to #)
12063  * @cfg {String} hrefTarget target frame for the link
12064  * @cfg {String} qtip An Ext QuickTip for the node
12065  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12066  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12067  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12068  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12069  * (defaults to undefined with no checkbox rendered)
12070  * @constructor
12071  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12072  */
12073 Roo.tree.TreeNode = function(attributes){
12074     attributes = attributes || {};
12075     if(typeof attributes == "string"){
12076         attributes = {text: attributes};
12077     }
12078     this.childrenRendered = false;
12079     this.rendered = false;
12080     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12081     this.expanded = attributes.expanded === true;
12082     this.isTarget = attributes.isTarget !== false;
12083     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12084     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12085
12086     /**
12087      * Read-only. The text for this node. To change it use setText().
12088      * @type String
12089      */
12090     this.text = attributes.text;
12091     /**
12092      * True if this node is disabled.
12093      * @type Boolean
12094      */
12095     this.disabled = attributes.disabled === true;
12096
12097     this.addEvents({
12098         /**
12099         * @event textchange
12100         * Fires when the text for this node is changed
12101         * @param {Node} this This node
12102         * @param {String} text The new text
12103         * @param {String} oldText The old text
12104         */
12105         "textchange" : true,
12106         /**
12107         * @event beforeexpand
12108         * Fires before this node is expanded, return false to cancel.
12109         * @param {Node} this This node
12110         * @param {Boolean} deep
12111         * @param {Boolean} anim
12112         */
12113         "beforeexpand" : true,
12114         /**
12115         * @event beforecollapse
12116         * Fires before this node is collapsed, return false to cancel.
12117         * @param {Node} this This node
12118         * @param {Boolean} deep
12119         * @param {Boolean} anim
12120         */
12121         "beforecollapse" : true,
12122         /**
12123         * @event expand
12124         * Fires when this node is expanded
12125         * @param {Node} this This node
12126         */
12127         "expand" : true,
12128         /**
12129         * @event disabledchange
12130         * Fires when the disabled status of this node changes
12131         * @param {Node} this This node
12132         * @param {Boolean} disabled
12133         */
12134         "disabledchange" : true,
12135         /**
12136         * @event collapse
12137         * Fires when this node is collapsed
12138         * @param {Node} this This node
12139         */
12140         "collapse" : true,
12141         /**
12142         * @event beforeclick
12143         * Fires before click processing. Return false to cancel the default action.
12144         * @param {Node} this This node
12145         * @param {Roo.EventObject} e The event object
12146         */
12147         "beforeclick":true,
12148         /**
12149         * @event checkchange
12150         * Fires when a node with a checkbox's checked property changes
12151         * @param {Node} this This node
12152         * @param {Boolean} checked
12153         */
12154         "checkchange":true,
12155         /**
12156         * @event click
12157         * Fires when this node is clicked
12158         * @param {Node} this This node
12159         * @param {Roo.EventObject} e The event object
12160         */
12161         "click":true,
12162         /**
12163         * @event dblclick
12164         * Fires when this node is double clicked
12165         * @param {Node} this This node
12166         * @param {Roo.EventObject} e The event object
12167         */
12168         "dblclick":true,
12169         /**
12170         * @event contextmenu
12171         * Fires when this node is right clicked
12172         * @param {Node} this This node
12173         * @param {Roo.EventObject} e The event object
12174         */
12175         "contextmenu":true,
12176         /**
12177         * @event beforechildrenrendered
12178         * Fires right before the child nodes for this node are rendered
12179         * @param {Node} this This node
12180         */
12181         "beforechildrenrendered":true
12182     });
12183
12184     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12185
12186     /**
12187      * Read-only. The UI for this node
12188      * @type TreeNodeUI
12189      */
12190     this.ui = new uiClass(this);
12191     
12192     // finally support items[]
12193     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12194         return;
12195     }
12196     
12197     
12198     Roo.each(this.attributes.items, function(c) {
12199         this.appendChild(Roo.factory(c,Roo.Tree));
12200     }, this);
12201     delete this.attributes.items;
12202     
12203     
12204     
12205 };
12206 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12207     preventHScroll: true,
12208     /**
12209      * Returns true if this node is expanded
12210      * @return {Boolean}
12211      */
12212     isExpanded : function(){
12213         return this.expanded;
12214     },
12215
12216     /**
12217      * Returns the UI object for this node
12218      * @return {TreeNodeUI}
12219      */
12220     getUI : function(){
12221         return this.ui;
12222     },
12223
12224     // private override
12225     setFirstChild : function(node){
12226         var of = this.firstChild;
12227         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12228         if(this.childrenRendered && of && node != of){
12229             of.renderIndent(true, true);
12230         }
12231         if(this.rendered){
12232             this.renderIndent(true, true);
12233         }
12234     },
12235
12236     // private override
12237     setLastChild : function(node){
12238         var ol = this.lastChild;
12239         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12240         if(this.childrenRendered && ol && node != ol){
12241             ol.renderIndent(true, true);
12242         }
12243         if(this.rendered){
12244             this.renderIndent(true, true);
12245         }
12246     },
12247
12248     // these methods are overridden to provide lazy rendering support
12249     // private override
12250     appendChild : function()
12251     {
12252         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12253         if(node && this.childrenRendered){
12254             node.render();
12255         }
12256         this.ui.updateExpandIcon();
12257         return node;
12258     },
12259
12260     // private override
12261     removeChild : function(node){
12262         this.ownerTree.getSelectionModel().unselect(node);
12263         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12264         // if it's been rendered remove dom node
12265         if(this.childrenRendered){
12266             node.ui.remove();
12267         }
12268         if(this.childNodes.length < 1){
12269             this.collapse(false, false);
12270         }else{
12271             this.ui.updateExpandIcon();
12272         }
12273         if(!this.firstChild) {
12274             this.childrenRendered = false;
12275         }
12276         return node;
12277     },
12278
12279     // private override
12280     insertBefore : function(node, refNode){
12281         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12282         if(newNode && refNode && this.childrenRendered){
12283             node.render();
12284         }
12285         this.ui.updateExpandIcon();
12286         return newNode;
12287     },
12288
12289     /**
12290      * Sets the text for this node
12291      * @param {String} text
12292      */
12293     setText : function(text){
12294         var oldText = this.text;
12295         this.text = text;
12296         this.attributes.text = text;
12297         if(this.rendered){ // event without subscribing
12298             this.ui.onTextChange(this, text, oldText);
12299         }
12300         this.fireEvent("textchange", this, text, oldText);
12301     },
12302
12303     /**
12304      * Triggers selection of this node
12305      */
12306     select : function(){
12307         this.getOwnerTree().getSelectionModel().select(this);
12308     },
12309
12310     /**
12311      * Triggers deselection of this node
12312      */
12313     unselect : function(){
12314         this.getOwnerTree().getSelectionModel().unselect(this);
12315     },
12316
12317     /**
12318      * Returns true if this node is selected
12319      * @return {Boolean}
12320      */
12321     isSelected : function(){
12322         return this.getOwnerTree().getSelectionModel().isSelected(this);
12323     },
12324
12325     /**
12326      * Expand this node.
12327      * @param {Boolean} deep (optional) True to expand all children as well
12328      * @param {Boolean} anim (optional) false to cancel the default animation
12329      * @param {Function} callback (optional) A callback to be called when
12330      * expanding this node completes (does not wait for deep expand to complete).
12331      * Called with 1 parameter, this node.
12332      */
12333     expand : function(deep, anim, callback){
12334         if(!this.expanded){
12335             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12336                 return;
12337             }
12338             if(!this.childrenRendered){
12339                 this.renderChildren();
12340             }
12341             this.expanded = true;
12342             
12343             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12344                 this.ui.animExpand(function(){
12345                     this.fireEvent("expand", this);
12346                     if(typeof callback == "function"){
12347                         callback(this);
12348                     }
12349                     if(deep === true){
12350                         this.expandChildNodes(true);
12351                     }
12352                 }.createDelegate(this));
12353                 return;
12354             }else{
12355                 this.ui.expand();
12356                 this.fireEvent("expand", this);
12357                 if(typeof callback == "function"){
12358                     callback(this);
12359                 }
12360             }
12361         }else{
12362            if(typeof callback == "function"){
12363                callback(this);
12364            }
12365         }
12366         if(deep === true){
12367             this.expandChildNodes(true);
12368         }
12369     },
12370
12371     isHiddenRoot : function(){
12372         return this.isRoot && !this.getOwnerTree().rootVisible;
12373     },
12374
12375     /**
12376      * Collapse this node.
12377      * @param {Boolean} deep (optional) True to collapse all children as well
12378      * @param {Boolean} anim (optional) false to cancel the default animation
12379      */
12380     collapse : function(deep, anim){
12381         if(this.expanded && !this.isHiddenRoot()){
12382             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12383                 return;
12384             }
12385             this.expanded = false;
12386             if((this.getOwnerTree().animate && anim !== false) || anim){
12387                 this.ui.animCollapse(function(){
12388                     this.fireEvent("collapse", this);
12389                     if(deep === true){
12390                         this.collapseChildNodes(true);
12391                     }
12392                 }.createDelegate(this));
12393                 return;
12394             }else{
12395                 this.ui.collapse();
12396                 this.fireEvent("collapse", this);
12397             }
12398         }
12399         if(deep === true){
12400             var cs = this.childNodes;
12401             for(var i = 0, len = cs.length; i < len; i++) {
12402                 cs[i].collapse(true, false);
12403             }
12404         }
12405     },
12406
12407     // private
12408     delayedExpand : function(delay){
12409         if(!this.expandProcId){
12410             this.expandProcId = this.expand.defer(delay, this);
12411         }
12412     },
12413
12414     // private
12415     cancelExpand : function(){
12416         if(this.expandProcId){
12417             clearTimeout(this.expandProcId);
12418         }
12419         this.expandProcId = false;
12420     },
12421
12422     /**
12423      * Toggles expanded/collapsed state of the node
12424      */
12425     toggle : function(){
12426         if(this.expanded){
12427             this.collapse();
12428         }else{
12429             this.expand();
12430         }
12431     },
12432
12433     /**
12434      * Ensures all parent nodes are expanded
12435      */
12436     ensureVisible : function(callback){
12437         var tree = this.getOwnerTree();
12438         tree.expandPath(this.parentNode.getPath(), false, function(){
12439             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12440             Roo.callback(callback);
12441         }.createDelegate(this));
12442     },
12443
12444     /**
12445      * Expand all child nodes
12446      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12447      */
12448     expandChildNodes : function(deep){
12449         var cs = this.childNodes;
12450         for(var i = 0, len = cs.length; i < len; i++) {
12451                 cs[i].expand(deep);
12452         }
12453     },
12454
12455     /**
12456      * Collapse all child nodes
12457      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12458      */
12459     collapseChildNodes : function(deep){
12460         var cs = this.childNodes;
12461         for(var i = 0, len = cs.length; i < len; i++) {
12462                 cs[i].collapse(deep);
12463         }
12464     },
12465
12466     /**
12467      * Disables this node
12468      */
12469     disable : function(){
12470         this.disabled = true;
12471         this.unselect();
12472         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12473             this.ui.onDisableChange(this, true);
12474         }
12475         this.fireEvent("disabledchange", this, true);
12476     },
12477
12478     /**
12479      * Enables this node
12480      */
12481     enable : function(){
12482         this.disabled = false;
12483         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12484             this.ui.onDisableChange(this, false);
12485         }
12486         this.fireEvent("disabledchange", this, false);
12487     },
12488
12489     // private
12490     renderChildren : function(suppressEvent){
12491         if(suppressEvent !== false){
12492             this.fireEvent("beforechildrenrendered", this);
12493         }
12494         var cs = this.childNodes;
12495         for(var i = 0, len = cs.length; i < len; i++){
12496             cs[i].render(true);
12497         }
12498         this.childrenRendered = true;
12499     },
12500
12501     // private
12502     sort : function(fn, scope){
12503         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12504         if(this.childrenRendered){
12505             var cs = this.childNodes;
12506             for(var i = 0, len = cs.length; i < len; i++){
12507                 cs[i].render(true);
12508             }
12509         }
12510     },
12511
12512     // private
12513     render : function(bulkRender){
12514         this.ui.render(bulkRender);
12515         if(!this.rendered){
12516             this.rendered = true;
12517             if(this.expanded){
12518                 this.expanded = false;
12519                 this.expand(false, false);
12520             }
12521         }
12522     },
12523
12524     // private
12525     renderIndent : function(deep, refresh){
12526         if(refresh){
12527             this.ui.childIndent = null;
12528         }
12529         this.ui.renderIndent();
12530         if(deep === true && this.childrenRendered){
12531             var cs = this.childNodes;
12532             for(var i = 0, len = cs.length; i < len; i++){
12533                 cs[i].renderIndent(true, refresh);
12534             }
12535         }
12536     }
12537 });/*
12538  * Based on:
12539  * Ext JS Library 1.1.1
12540  * Copyright(c) 2006-2007, Ext JS, LLC.
12541  *
12542  * Originally Released Under LGPL - original licence link has changed is not relivant.
12543  *
12544  * Fork - LGPL
12545  * <script type="text/javascript">
12546  */
12547  
12548 /**
12549  * @class Roo.tree.AsyncTreeNode
12550  * @extends Roo.tree.TreeNode
12551  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12552  * @constructor
12553  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12554  */
12555  Roo.tree.AsyncTreeNode = function(config){
12556     this.loaded = false;
12557     this.loading = false;
12558     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12559     /**
12560     * @event beforeload
12561     * Fires before this node is loaded, return false to cancel
12562     * @param {Node} this This node
12563     */
12564     this.addEvents({'beforeload':true, 'load': true});
12565     /**
12566     * @event load
12567     * Fires when this node is loaded
12568     * @param {Node} this This node
12569     */
12570     /**
12571      * The loader used by this node (defaults to using the tree's defined loader)
12572      * @type TreeLoader
12573      * @property loader
12574      */
12575 };
12576 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12577     expand : function(deep, anim, callback){
12578         if(this.loading){ // if an async load is already running, waiting til it's done
12579             var timer;
12580             var f = function(){
12581                 if(!this.loading){ // done loading
12582                     clearInterval(timer);
12583                     this.expand(deep, anim, callback);
12584                 }
12585             }.createDelegate(this);
12586             timer = setInterval(f, 200);
12587             return;
12588         }
12589         if(!this.loaded){
12590             if(this.fireEvent("beforeload", this) === false){
12591                 return;
12592             }
12593             this.loading = true;
12594             this.ui.beforeLoad(this);
12595             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12596             if(loader){
12597                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12598                 return;
12599             }
12600         }
12601         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12602     },
12603     
12604     /**
12605      * Returns true if this node is currently loading
12606      * @return {Boolean}
12607      */
12608     isLoading : function(){
12609         return this.loading;  
12610     },
12611     
12612     loadComplete : function(deep, anim, callback){
12613         this.loading = false;
12614         this.loaded = true;
12615         this.ui.afterLoad(this);
12616         this.fireEvent("load", this);
12617         this.expand(deep, anim, callback);
12618     },
12619     
12620     /**
12621      * Returns true if this node has been loaded
12622      * @return {Boolean}
12623      */
12624     isLoaded : function(){
12625         return this.loaded;
12626     },
12627     
12628     hasChildNodes : function(){
12629         if(!this.isLeaf() && !this.loaded){
12630             return true;
12631         }else{
12632             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12633         }
12634     },
12635
12636     /**
12637      * Trigger a reload for this node
12638      * @param {Function} callback
12639      */
12640     reload : function(callback){
12641         this.collapse(false, false);
12642         while(this.firstChild){
12643             this.removeChild(this.firstChild);
12644         }
12645         this.childrenRendered = false;
12646         this.loaded = false;
12647         if(this.isHiddenRoot()){
12648             this.expanded = false;
12649         }
12650         this.expand(false, false, callback);
12651     }
12652 });/*
12653  * Based on:
12654  * Ext JS Library 1.1.1
12655  * Copyright(c) 2006-2007, Ext JS, LLC.
12656  *
12657  * Originally Released Under LGPL - original licence link has changed is not relivant.
12658  *
12659  * Fork - LGPL
12660  * <script type="text/javascript">
12661  */
12662  
12663 /**
12664  * @class Roo.tree.TreeNodeUI
12665  * @constructor
12666  * @param {Object} node The node to render
12667  * The TreeNode UI implementation is separate from the
12668  * tree implementation. Unless you are customizing the tree UI,
12669  * you should never have to use this directly.
12670  */
12671 Roo.tree.TreeNodeUI = function(node){
12672     this.node = node;
12673     this.rendered = false;
12674     this.animating = false;
12675     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12676 };
12677
12678 Roo.tree.TreeNodeUI.prototype = {
12679     removeChild : function(node){
12680         if(this.rendered){
12681             this.ctNode.removeChild(node.ui.getEl());
12682         }
12683     },
12684
12685     beforeLoad : function(){
12686          this.addClass("x-tree-node-loading");
12687     },
12688
12689     afterLoad : function(){
12690          this.removeClass("x-tree-node-loading");
12691     },
12692
12693     onTextChange : function(node, text, oldText){
12694         if(this.rendered){
12695             this.textNode.innerHTML = text;
12696         }
12697     },
12698
12699     onDisableChange : function(node, state){
12700         this.disabled = state;
12701         if(state){
12702             this.addClass("x-tree-node-disabled");
12703         }else{
12704             this.removeClass("x-tree-node-disabled");
12705         }
12706     },
12707
12708     onSelectedChange : function(state){
12709         if(state){
12710             this.focus();
12711             this.addClass("x-tree-selected");
12712         }else{
12713             //this.blur();
12714             this.removeClass("x-tree-selected");
12715         }
12716     },
12717
12718     onMove : function(tree, node, oldParent, newParent, index, refNode){
12719         this.childIndent = null;
12720         if(this.rendered){
12721             var targetNode = newParent.ui.getContainer();
12722             if(!targetNode){//target not rendered
12723                 this.holder = document.createElement("div");
12724                 this.holder.appendChild(this.wrap);
12725                 return;
12726             }
12727             var insertBefore = refNode ? refNode.ui.getEl() : null;
12728             if(insertBefore){
12729                 targetNode.insertBefore(this.wrap, insertBefore);
12730             }else{
12731                 targetNode.appendChild(this.wrap);
12732             }
12733             this.node.renderIndent(true);
12734         }
12735     },
12736
12737     addClass : function(cls){
12738         if(this.elNode){
12739             Roo.fly(this.elNode).addClass(cls);
12740         }
12741     },
12742
12743     removeClass : function(cls){
12744         if(this.elNode){
12745             Roo.fly(this.elNode).removeClass(cls);
12746         }
12747     },
12748
12749     remove : function(){
12750         if(this.rendered){
12751             this.holder = document.createElement("div");
12752             this.holder.appendChild(this.wrap);
12753         }
12754     },
12755
12756     fireEvent : function(){
12757         return this.node.fireEvent.apply(this.node, arguments);
12758     },
12759
12760     initEvents : function(){
12761         this.node.on("move", this.onMove, this);
12762         var E = Roo.EventManager;
12763         var a = this.anchor;
12764
12765         var el = Roo.fly(a, '_treeui');
12766
12767         if(Roo.isOpera){ // opera render bug ignores the CSS
12768             el.setStyle("text-decoration", "none");
12769         }
12770
12771         el.on("click", this.onClick, this);
12772         el.on("dblclick", this.onDblClick, this);
12773
12774         if(this.checkbox){
12775             Roo.EventManager.on(this.checkbox,
12776                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12777         }
12778
12779         el.on("contextmenu", this.onContextMenu, this);
12780
12781         var icon = Roo.fly(this.iconNode);
12782         icon.on("click", this.onClick, this);
12783         icon.on("dblclick", this.onDblClick, this);
12784         icon.on("contextmenu", this.onContextMenu, this);
12785         E.on(this.ecNode, "click", this.ecClick, this, true);
12786
12787         if(this.node.disabled){
12788             this.addClass("x-tree-node-disabled");
12789         }
12790         if(this.node.hidden){
12791             this.addClass("x-tree-node-disabled");
12792         }
12793         var ot = this.node.getOwnerTree();
12794         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12795         if(dd && (!this.node.isRoot || ot.rootVisible)){
12796             Roo.dd.Registry.register(this.elNode, {
12797                 node: this.node,
12798                 handles: this.getDDHandles(),
12799                 isHandle: false
12800             });
12801         }
12802     },
12803
12804     getDDHandles : function(){
12805         return [this.iconNode, this.textNode];
12806     },
12807
12808     hide : function(){
12809         if(this.rendered){
12810             this.wrap.style.display = "none";
12811         }
12812     },
12813
12814     show : function(){
12815         if(this.rendered){
12816             this.wrap.style.display = "";
12817         }
12818     },
12819
12820     onContextMenu : function(e){
12821         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12822             e.preventDefault();
12823             this.focus();
12824             this.fireEvent("contextmenu", this.node, e);
12825         }
12826     },
12827
12828     onClick : function(e){
12829         if(this.dropping){
12830             e.stopEvent();
12831             return;
12832         }
12833         if(this.fireEvent("beforeclick", this.node, e) !== false){
12834             if(!this.disabled && this.node.attributes.href){
12835                 this.fireEvent("click", this.node, e);
12836                 return;
12837             }
12838             e.preventDefault();
12839             if(this.disabled){
12840                 return;
12841             }
12842
12843             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12844                 this.node.toggle();
12845             }
12846
12847             this.fireEvent("click", this.node, e);
12848         }else{
12849             e.stopEvent();
12850         }
12851     },
12852
12853     onDblClick : function(e){
12854         e.preventDefault();
12855         if(this.disabled){
12856             return;
12857         }
12858         if(this.checkbox){
12859             this.toggleCheck();
12860         }
12861         if(!this.animating && this.node.hasChildNodes()){
12862             this.node.toggle();
12863         }
12864         this.fireEvent("dblclick", this.node, e);
12865     },
12866
12867     onCheckChange : function(){
12868         var checked = this.checkbox.checked;
12869         this.node.attributes.checked = checked;
12870         this.fireEvent('checkchange', this.node, checked);
12871     },
12872
12873     ecClick : function(e){
12874         if(!this.animating && this.node.hasChildNodes()){
12875             this.node.toggle();
12876         }
12877     },
12878
12879     startDrop : function(){
12880         this.dropping = true;
12881     },
12882
12883     // delayed drop so the click event doesn't get fired on a drop
12884     endDrop : function(){
12885        setTimeout(function(){
12886            this.dropping = false;
12887        }.createDelegate(this), 50);
12888     },
12889
12890     expand : function(){
12891         this.updateExpandIcon();
12892         this.ctNode.style.display = "";
12893     },
12894
12895     focus : function(){
12896         if(!this.node.preventHScroll){
12897             try{this.anchor.focus();
12898             }catch(e){}
12899         }else if(!Roo.isIE){
12900             try{
12901                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12902                 var l = noscroll.scrollLeft;
12903                 this.anchor.focus();
12904                 noscroll.scrollLeft = l;
12905             }catch(e){}
12906         }
12907     },
12908
12909     toggleCheck : function(value){
12910         var cb = this.checkbox;
12911         if(cb){
12912             cb.checked = (value === undefined ? !cb.checked : value);
12913         }
12914     },
12915
12916     blur : function(){
12917         try{
12918             this.anchor.blur();
12919         }catch(e){}
12920     },
12921
12922     animExpand : function(callback){
12923         var ct = Roo.get(this.ctNode);
12924         ct.stopFx();
12925         if(!this.node.hasChildNodes()){
12926             this.updateExpandIcon();
12927             this.ctNode.style.display = "";
12928             Roo.callback(callback);
12929             return;
12930         }
12931         this.animating = true;
12932         this.updateExpandIcon();
12933
12934         ct.slideIn('t', {
12935            callback : function(){
12936                this.animating = false;
12937                Roo.callback(callback);
12938             },
12939             scope: this,
12940             duration: this.node.ownerTree.duration || .25
12941         });
12942     },
12943
12944     highlight : function(){
12945         var tree = this.node.getOwnerTree();
12946         Roo.fly(this.wrap).highlight(
12947             tree.hlColor || "C3DAF9",
12948             {endColor: tree.hlBaseColor}
12949         );
12950     },
12951
12952     collapse : function(){
12953         this.updateExpandIcon();
12954         this.ctNode.style.display = "none";
12955     },
12956
12957     animCollapse : function(callback){
12958         var ct = Roo.get(this.ctNode);
12959         ct.enableDisplayMode('block');
12960         ct.stopFx();
12961
12962         this.animating = true;
12963         this.updateExpandIcon();
12964
12965         ct.slideOut('t', {
12966             callback : function(){
12967                this.animating = false;
12968                Roo.callback(callback);
12969             },
12970             scope: this,
12971             duration: this.node.ownerTree.duration || .25
12972         });
12973     },
12974
12975     getContainer : function(){
12976         return this.ctNode;
12977     },
12978
12979     getEl : function(){
12980         return this.wrap;
12981     },
12982
12983     appendDDGhost : function(ghostNode){
12984         ghostNode.appendChild(this.elNode.cloneNode(true));
12985     },
12986
12987     getDDRepairXY : function(){
12988         return Roo.lib.Dom.getXY(this.iconNode);
12989     },
12990
12991     onRender : function(){
12992         this.render();
12993     },
12994
12995     render : function(bulkRender){
12996         var n = this.node, a = n.attributes;
12997         var targetNode = n.parentNode ?
12998               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12999
13000         if(!this.rendered){
13001             this.rendered = true;
13002
13003             this.renderElements(n, a, targetNode, bulkRender);
13004
13005             if(a.qtip){
13006                if(this.textNode.setAttributeNS){
13007                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13008                    if(a.qtipTitle){
13009                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13010                    }
13011                }else{
13012                    this.textNode.setAttribute("ext:qtip", a.qtip);
13013                    if(a.qtipTitle){
13014                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13015                    }
13016                }
13017             }else if(a.qtipCfg){
13018                 a.qtipCfg.target = Roo.id(this.textNode);
13019                 Roo.QuickTips.register(a.qtipCfg);
13020             }
13021             this.initEvents();
13022             if(!this.node.expanded){
13023                 this.updateExpandIcon();
13024             }
13025         }else{
13026             if(bulkRender === true) {
13027                 targetNode.appendChild(this.wrap);
13028             }
13029         }
13030     },
13031
13032     renderElements : function(n, a, targetNode, bulkRender)
13033     {
13034         // add some indent caching, this helps performance when rendering a large tree
13035         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13036         var t = n.getOwnerTree();
13037         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13038         if (typeof(n.attributes.html) != 'undefined') {
13039             txt = n.attributes.html;
13040         }
13041         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13042         var cb = typeof a.checked == 'boolean';
13043         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13044         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13045             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13046             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13047             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13048             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13049             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13050              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13051                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13052             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13053             "</li>"];
13054
13055         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13056             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13057                                 n.nextSibling.ui.getEl(), buf.join(""));
13058         }else{
13059             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13060         }
13061
13062         this.elNode = this.wrap.childNodes[0];
13063         this.ctNode = this.wrap.childNodes[1];
13064         var cs = this.elNode.childNodes;
13065         this.indentNode = cs[0];
13066         this.ecNode = cs[1];
13067         this.iconNode = cs[2];
13068         var index = 3;
13069         if(cb){
13070             this.checkbox = cs[3];
13071             index++;
13072         }
13073         this.anchor = cs[index];
13074         this.textNode = cs[index].firstChild;
13075     },
13076
13077     getAnchor : function(){
13078         return this.anchor;
13079     },
13080
13081     getTextEl : function(){
13082         return this.textNode;
13083     },
13084
13085     getIconEl : function(){
13086         return this.iconNode;
13087     },
13088
13089     isChecked : function(){
13090         return this.checkbox ? this.checkbox.checked : false;
13091     },
13092
13093     updateExpandIcon : function(){
13094         if(this.rendered){
13095             var n = this.node, c1, c2;
13096             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13097             var hasChild = n.hasChildNodes();
13098             if(hasChild){
13099                 if(n.expanded){
13100                     cls += "-minus";
13101                     c1 = "x-tree-node-collapsed";
13102                     c2 = "x-tree-node-expanded";
13103                 }else{
13104                     cls += "-plus";
13105                     c1 = "x-tree-node-expanded";
13106                     c2 = "x-tree-node-collapsed";
13107                 }
13108                 if(this.wasLeaf){
13109                     this.removeClass("x-tree-node-leaf");
13110                     this.wasLeaf = false;
13111                 }
13112                 if(this.c1 != c1 || this.c2 != c2){
13113                     Roo.fly(this.elNode).replaceClass(c1, c2);
13114                     this.c1 = c1; this.c2 = c2;
13115                 }
13116             }else{
13117                 // this changes non-leafs into leafs if they have no children.
13118                 // it's not very rational behaviour..
13119                 
13120                 if(!this.wasLeaf && this.node.leaf){
13121                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13122                     delete this.c1;
13123                     delete this.c2;
13124                     this.wasLeaf = true;
13125                 }
13126             }
13127             var ecc = "x-tree-ec-icon "+cls;
13128             if(this.ecc != ecc){
13129                 this.ecNode.className = ecc;
13130                 this.ecc = ecc;
13131             }
13132         }
13133     },
13134
13135     getChildIndent : function(){
13136         if(!this.childIndent){
13137             var buf = [];
13138             var p = this.node;
13139             while(p){
13140                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13141                     if(!p.isLast()) {
13142                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13143                     } else {
13144                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13145                     }
13146                 }
13147                 p = p.parentNode;
13148             }
13149             this.childIndent = buf.join("");
13150         }
13151         return this.childIndent;
13152     },
13153
13154     renderIndent : function(){
13155         if(this.rendered){
13156             var indent = "";
13157             var p = this.node.parentNode;
13158             if(p){
13159                 indent = p.ui.getChildIndent();
13160             }
13161             if(this.indentMarkup != indent){ // don't rerender if not required
13162                 this.indentNode.innerHTML = indent;
13163                 this.indentMarkup = indent;
13164             }
13165             this.updateExpandIcon();
13166         }
13167     }
13168 };
13169
13170 Roo.tree.RootTreeNodeUI = function(){
13171     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13172 };
13173 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13174     render : function(){
13175         if(!this.rendered){
13176             var targetNode = this.node.ownerTree.innerCt.dom;
13177             this.node.expanded = true;
13178             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13179             this.wrap = this.ctNode = targetNode.firstChild;
13180         }
13181     },
13182     collapse : function(){
13183     },
13184     expand : function(){
13185     }
13186 });/*
13187  * Based on:
13188  * Ext JS Library 1.1.1
13189  * Copyright(c) 2006-2007, Ext JS, LLC.
13190  *
13191  * Originally Released Under LGPL - original licence link has changed is not relivant.
13192  *
13193  * Fork - LGPL
13194  * <script type="text/javascript">
13195  */
13196 /**
13197  * @class Roo.tree.TreeLoader
13198  * @extends Roo.util.Observable
13199  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13200  * nodes from a specified URL. The response must be a javascript Array definition
13201  * who's elements are node definition objects. eg:
13202  * <pre><code>
13203 {  success : true,
13204    data :      [
13205    
13206     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13207     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13208     ]
13209 }
13210
13211
13212 </code></pre>
13213  * <br><br>
13214  * The old style respose with just an array is still supported, but not recommended.
13215  * <br><br>
13216  *
13217  * A server request is sent, and child nodes are loaded only when a node is expanded.
13218  * The loading node's id is passed to the server under the parameter name "node" to
13219  * enable the server to produce the correct child nodes.
13220  * <br><br>
13221  * To pass extra parameters, an event handler may be attached to the "beforeload"
13222  * event, and the parameters specified in the TreeLoader's baseParams property:
13223  * <pre><code>
13224     myTreeLoader.on("beforeload", function(treeLoader, node) {
13225         this.baseParams.category = node.attributes.category;
13226     }, this);
13227     
13228 </code></pre>
13229  *
13230  * This would pass an HTTP parameter called "category" to the server containing
13231  * the value of the Node's "category" attribute.
13232  * @constructor
13233  * Creates a new Treeloader.
13234  * @param {Object} config A config object containing config properties.
13235  */
13236 Roo.tree.TreeLoader = function(config){
13237     this.baseParams = {};
13238     this.requestMethod = "POST";
13239     Roo.apply(this, config);
13240
13241     this.addEvents({
13242     
13243         /**
13244          * @event beforeload
13245          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13246          * @param {Object} This TreeLoader object.
13247          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13248          * @param {Object} callback The callback function specified in the {@link #load} call.
13249          */
13250         beforeload : true,
13251         /**
13252          * @event load
13253          * Fires when the node has been successfuly loaded.
13254          * @param {Object} This TreeLoader object.
13255          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13256          * @param {Object} response The response object containing the data from the server.
13257          */
13258         load : true,
13259         /**
13260          * @event loadexception
13261          * Fires if the network request failed.
13262          * @param {Object} This TreeLoader object.
13263          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13264          * @param {Object} response The response object containing the data from the server.
13265          */
13266         loadexception : true,
13267         /**
13268          * @event create
13269          * Fires before a node is created, enabling you to return custom Node types 
13270          * @param {Object} This TreeLoader object.
13271          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13272          */
13273         create : true
13274     });
13275
13276     Roo.tree.TreeLoader.superclass.constructor.call(this);
13277 };
13278
13279 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13280     /**
13281     * @cfg {String} dataUrl The URL from which to request a Json string which
13282     * specifies an array of node definition object representing the child nodes
13283     * to be loaded.
13284     */
13285     /**
13286     * @cfg {String} requestMethod either GET or POST
13287     * defaults to POST (due to BC)
13288     * to be loaded.
13289     */
13290     /**
13291     * @cfg {Object} baseParams (optional) An object containing properties which
13292     * specify HTTP parameters to be passed to each request for child nodes.
13293     */
13294     /**
13295     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13296     * created by this loader. If the attributes sent by the server have an attribute in this object,
13297     * they take priority.
13298     */
13299     /**
13300     * @cfg {Object} uiProviders (optional) An object containing properties which
13301     * 
13302     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13303     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13304     * <i>uiProvider</i> attribute of a returned child node is a string rather
13305     * than a reference to a TreeNodeUI implementation, this that string value
13306     * is used as a property name in the uiProviders object. You can define the provider named
13307     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13308     */
13309     uiProviders : {},
13310
13311     /**
13312     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13313     * child nodes before loading.
13314     */
13315     clearOnLoad : true,
13316
13317     /**
13318     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13319     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13320     * Grid query { data : [ .....] }
13321     */
13322     
13323     root : false,
13324      /**
13325     * @cfg {String} queryParam (optional) 
13326     * Name of the query as it will be passed on the querystring (defaults to 'node')
13327     * eg. the request will be ?node=[id]
13328     */
13329     
13330     
13331     queryParam: false,
13332     
13333     /**
13334      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13335      * This is called automatically when a node is expanded, but may be used to reload
13336      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13337      * @param {Roo.tree.TreeNode} node
13338      * @param {Function} callback
13339      */
13340     load : function(node, callback){
13341         if(this.clearOnLoad){
13342             while(node.firstChild){
13343                 node.removeChild(node.firstChild);
13344             }
13345         }
13346         if(node.attributes.children){ // preloaded json children
13347             var cs = node.attributes.children;
13348             for(var i = 0, len = cs.length; i < len; i++){
13349                 node.appendChild(this.createNode(cs[i]));
13350             }
13351             if(typeof callback == "function"){
13352                 callback();
13353             }
13354         }else if(this.dataUrl){
13355             this.requestData(node, callback);
13356         }
13357     },
13358
13359     getParams: function(node){
13360         var buf = [], bp = this.baseParams;
13361         for(var key in bp){
13362             if(typeof bp[key] != "function"){
13363                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13364             }
13365         }
13366         var n = this.queryParam === false ? 'node' : this.queryParam;
13367         buf.push(n + "=", encodeURIComponent(node.id));
13368         return buf.join("");
13369     },
13370
13371     requestData : function(node, callback){
13372         if(this.fireEvent("beforeload", this, node, callback) !== false){
13373             this.transId = Roo.Ajax.request({
13374                 method:this.requestMethod,
13375                 url: this.dataUrl||this.url,
13376                 success: this.handleResponse,
13377                 failure: this.handleFailure,
13378                 scope: this,
13379                 argument: {callback: callback, node: node},
13380                 params: this.getParams(node)
13381             });
13382         }else{
13383             // if the load is cancelled, make sure we notify
13384             // the node that we are done
13385             if(typeof callback == "function"){
13386                 callback();
13387             }
13388         }
13389     },
13390
13391     isLoading : function(){
13392         return this.transId ? true : false;
13393     },
13394
13395     abort : function(){
13396         if(this.isLoading()){
13397             Roo.Ajax.abort(this.transId);
13398         }
13399     },
13400
13401     // private
13402     createNode : function(attr)
13403     {
13404         // apply baseAttrs, nice idea Corey!
13405         if(this.baseAttrs){
13406             Roo.applyIf(attr, this.baseAttrs);
13407         }
13408         if(this.applyLoader !== false){
13409             attr.loader = this;
13410         }
13411         // uiProvider = depreciated..
13412         
13413         if(typeof(attr.uiProvider) == 'string'){
13414            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13415                 /**  eval:var:attr */ eval(attr.uiProvider);
13416         }
13417         if(typeof(this.uiProviders['default']) != 'undefined') {
13418             attr.uiProvider = this.uiProviders['default'];
13419         }
13420         
13421         this.fireEvent('create', this, attr);
13422         
13423         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13424         return(attr.leaf ?
13425                         new Roo.tree.TreeNode(attr) :
13426                         new Roo.tree.AsyncTreeNode(attr));
13427     },
13428
13429     processResponse : function(response, node, callback)
13430     {
13431         var json = response.responseText;
13432         try {
13433             
13434             var o = Roo.decode(json);
13435             
13436             if (this.root === false && typeof(o.success) != undefined) {
13437                 this.root = 'data'; // the default behaviour for list like data..
13438                 }
13439                 
13440             if (this.root !== false &&  !o.success) {
13441                 // it's a failure condition.
13442                 var a = response.argument;
13443                 this.fireEvent("loadexception", this, a.node, response);
13444                 Roo.log("Load failed - should have a handler really");
13445                 return;
13446             }
13447             
13448             
13449             
13450             if (this.root !== false) {
13451                  o = o[this.root];
13452             }
13453             
13454             for(var i = 0, len = o.length; i < len; i++){
13455                 var n = this.createNode(o[i]);
13456                 if(n){
13457                     node.appendChild(n);
13458                 }
13459             }
13460             if(typeof callback == "function"){
13461                 callback(this, node);
13462             }
13463         }catch(e){
13464             this.handleFailure(response);
13465         }
13466     },
13467
13468     handleResponse : function(response){
13469         this.transId = false;
13470         var a = response.argument;
13471         this.processResponse(response, a.node, a.callback);
13472         this.fireEvent("load", this, a.node, response);
13473     },
13474
13475     handleFailure : function(response)
13476     {
13477         // should handle failure better..
13478         this.transId = false;
13479         var a = response.argument;
13480         this.fireEvent("loadexception", this, a.node, response);
13481         if(typeof a.callback == "function"){
13482             a.callback(this, a.node);
13483         }
13484     }
13485 });/*
13486  * Based on:
13487  * Ext JS Library 1.1.1
13488  * Copyright(c) 2006-2007, Ext JS, LLC.
13489  *
13490  * Originally Released Under LGPL - original licence link has changed is not relivant.
13491  *
13492  * Fork - LGPL
13493  * <script type="text/javascript">
13494  */
13495
13496 /**
13497 * @class Roo.tree.TreeFilter
13498 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13499 * @param {TreePanel} tree
13500 * @param {Object} config (optional)
13501  */
13502 Roo.tree.TreeFilter = function(tree, config){
13503     this.tree = tree;
13504     this.filtered = {};
13505     Roo.apply(this, config);
13506 };
13507
13508 Roo.tree.TreeFilter.prototype = {
13509     clearBlank:false,
13510     reverse:false,
13511     autoClear:false,
13512     remove:false,
13513
13514      /**
13515      * Filter the data by a specific attribute.
13516      * @param {String/RegExp} value Either string that the attribute value
13517      * should start with or a RegExp to test against the attribute
13518      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13519      * @param {TreeNode} startNode (optional) The node to start the filter at.
13520      */
13521     filter : function(value, attr, startNode){
13522         attr = attr || "text";
13523         var f;
13524         if(typeof value == "string"){
13525             var vlen = value.length;
13526             // auto clear empty filter
13527             if(vlen == 0 && this.clearBlank){
13528                 this.clear();
13529                 return;
13530             }
13531             value = value.toLowerCase();
13532             f = function(n){
13533                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13534             };
13535         }else if(value.exec){ // regex?
13536             f = function(n){
13537                 return value.test(n.attributes[attr]);
13538             };
13539         }else{
13540             throw 'Illegal filter type, must be string or regex';
13541         }
13542         this.filterBy(f, null, startNode);
13543         },
13544
13545     /**
13546      * Filter by a function. The passed function will be called with each
13547      * node in the tree (or from the startNode). If the function returns true, the node is kept
13548      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13549      * @param {Function} fn The filter function
13550      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13551      */
13552     filterBy : function(fn, scope, startNode){
13553         startNode = startNode || this.tree.root;
13554         if(this.autoClear){
13555             this.clear();
13556         }
13557         var af = this.filtered, rv = this.reverse;
13558         var f = function(n){
13559             if(n == startNode){
13560                 return true;
13561             }
13562             if(af[n.id]){
13563                 return false;
13564             }
13565             var m = fn.call(scope || n, n);
13566             if(!m || rv){
13567                 af[n.id] = n;
13568                 n.ui.hide();
13569                 return false;
13570             }
13571             return true;
13572         };
13573         startNode.cascade(f);
13574         if(this.remove){
13575            for(var id in af){
13576                if(typeof id != "function"){
13577                    var n = af[id];
13578                    if(n && n.parentNode){
13579                        n.parentNode.removeChild(n);
13580                    }
13581                }
13582            }
13583         }
13584     },
13585
13586     /**
13587      * Clears the current filter. Note: with the "remove" option
13588      * set a filter cannot be cleared.
13589      */
13590     clear : function(){
13591         var t = this.tree;
13592         var af = this.filtered;
13593         for(var id in af){
13594             if(typeof id != "function"){
13595                 var n = af[id];
13596                 if(n){
13597                     n.ui.show();
13598                 }
13599             }
13600         }
13601         this.filtered = {};
13602     }
13603 };
13604 /*
13605  * Based on:
13606  * Ext JS Library 1.1.1
13607  * Copyright(c) 2006-2007, Ext JS, LLC.
13608  *
13609  * Originally Released Under LGPL - original licence link has changed is not relivant.
13610  *
13611  * Fork - LGPL
13612  * <script type="text/javascript">
13613  */
13614  
13615
13616 /**
13617  * @class Roo.tree.TreeSorter
13618  * Provides sorting of nodes in a TreePanel
13619  * 
13620  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13621  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13622  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13623  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13624  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13625  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13626  * @constructor
13627  * @param {TreePanel} tree
13628  * @param {Object} config
13629  */
13630 Roo.tree.TreeSorter = function(tree, config){
13631     Roo.apply(this, config);
13632     tree.on("beforechildrenrendered", this.doSort, this);
13633     tree.on("append", this.updateSort, this);
13634     tree.on("insert", this.updateSort, this);
13635     
13636     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13637     var p = this.property || "text";
13638     var sortType = this.sortType;
13639     var fs = this.folderSort;
13640     var cs = this.caseSensitive === true;
13641     var leafAttr = this.leafAttr || 'leaf';
13642
13643     this.sortFn = function(n1, n2){
13644         if(fs){
13645             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13646                 return 1;
13647             }
13648             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13649                 return -1;
13650             }
13651         }
13652         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13653         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13654         if(v1 < v2){
13655                         return dsc ? +1 : -1;
13656                 }else if(v1 > v2){
13657                         return dsc ? -1 : +1;
13658         }else{
13659                 return 0;
13660         }
13661     };
13662 };
13663
13664 Roo.tree.TreeSorter.prototype = {
13665     doSort : function(node){
13666         node.sort(this.sortFn);
13667     },
13668     
13669     compareNodes : function(n1, n2){
13670         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13671     },
13672     
13673     updateSort : function(tree, node){
13674         if(node.childrenRendered){
13675             this.doSort.defer(1, this, [node]);
13676         }
13677     }
13678 };/*
13679  * Based on:
13680  * Ext JS Library 1.1.1
13681  * Copyright(c) 2006-2007, Ext JS, LLC.
13682  *
13683  * Originally Released Under LGPL - original licence link has changed is not relivant.
13684  *
13685  * Fork - LGPL
13686  * <script type="text/javascript">
13687  */
13688
13689 if(Roo.dd.DropZone){
13690     
13691 Roo.tree.TreeDropZone = function(tree, config){
13692     this.allowParentInsert = false;
13693     this.allowContainerDrop = false;
13694     this.appendOnly = false;
13695     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13696     this.tree = tree;
13697     this.lastInsertClass = "x-tree-no-status";
13698     this.dragOverData = {};
13699 };
13700
13701 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13702     ddGroup : "TreeDD",
13703     scroll:  true,
13704     
13705     expandDelay : 1000,
13706     
13707     expandNode : function(node){
13708         if(node.hasChildNodes() && !node.isExpanded()){
13709             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13710         }
13711     },
13712     
13713     queueExpand : function(node){
13714         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13715     },
13716     
13717     cancelExpand : function(){
13718         if(this.expandProcId){
13719             clearTimeout(this.expandProcId);
13720             this.expandProcId = false;
13721         }
13722     },
13723     
13724     isValidDropPoint : function(n, pt, dd, e, data){
13725         if(!n || !data){ return false; }
13726         var targetNode = n.node;
13727         var dropNode = data.node;
13728         // default drop rules
13729         if(!(targetNode && targetNode.isTarget && pt)){
13730             return false;
13731         }
13732         if(pt == "append" && targetNode.allowChildren === false){
13733             return false;
13734         }
13735         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13736             return false;
13737         }
13738         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13739             return false;
13740         }
13741         // reuse the object
13742         var overEvent = this.dragOverData;
13743         overEvent.tree = this.tree;
13744         overEvent.target = targetNode;
13745         overEvent.data = data;
13746         overEvent.point = pt;
13747         overEvent.source = dd;
13748         overEvent.rawEvent = e;
13749         overEvent.dropNode = dropNode;
13750         overEvent.cancel = false;  
13751         var result = this.tree.fireEvent("nodedragover", overEvent);
13752         return overEvent.cancel === false && result !== false;
13753     },
13754     
13755     getDropPoint : function(e, n, dd)
13756     {
13757         var tn = n.node;
13758         if(tn.isRoot){
13759             return tn.allowChildren !== false ? "append" : false; // always append for root
13760         }
13761         var dragEl = n.ddel;
13762         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13763         var y = Roo.lib.Event.getPageY(e);
13764         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13765         
13766         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13767         var noAppend = tn.allowChildren === false;
13768         if(this.appendOnly || tn.parentNode.allowChildren === false){
13769             return noAppend ? false : "append";
13770         }
13771         var noBelow = false;
13772         if(!this.allowParentInsert){
13773             noBelow = tn.hasChildNodes() && tn.isExpanded();
13774         }
13775         var q = (b - t) / (noAppend ? 2 : 3);
13776         if(y >= t && y < (t + q)){
13777             return "above";
13778         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13779             return "below";
13780         }else{
13781             return "append";
13782         }
13783     },
13784     
13785     onNodeEnter : function(n, dd, e, data)
13786     {
13787         this.cancelExpand();
13788     },
13789     
13790     onNodeOver : function(n, dd, e, data)
13791     {
13792        
13793         var pt = this.getDropPoint(e, n, dd);
13794         var node = n.node;
13795         
13796         // auto node expand check
13797         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13798             this.queueExpand(node);
13799         }else if(pt != "append"){
13800             this.cancelExpand();
13801         }
13802         
13803         // set the insert point style on the target node
13804         var returnCls = this.dropNotAllowed;
13805         if(this.isValidDropPoint(n, pt, dd, e, data)){
13806            if(pt){
13807                var el = n.ddel;
13808                var cls;
13809                if(pt == "above"){
13810                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13811                    cls = "x-tree-drag-insert-above";
13812                }else if(pt == "below"){
13813                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13814                    cls = "x-tree-drag-insert-below";
13815                }else{
13816                    returnCls = "x-tree-drop-ok-append";
13817                    cls = "x-tree-drag-append";
13818                }
13819                if(this.lastInsertClass != cls){
13820                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13821                    this.lastInsertClass = cls;
13822                }
13823            }
13824        }
13825        return returnCls;
13826     },
13827     
13828     onNodeOut : function(n, dd, e, data){
13829         
13830         this.cancelExpand();
13831         this.removeDropIndicators(n);
13832     },
13833     
13834     onNodeDrop : function(n, dd, e, data){
13835         var point = this.getDropPoint(e, n, dd);
13836         var targetNode = n.node;
13837         targetNode.ui.startDrop();
13838         if(!this.isValidDropPoint(n, point, dd, e, data)){
13839             targetNode.ui.endDrop();
13840             return false;
13841         }
13842         // first try to find the drop node
13843         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13844         var dropEvent = {
13845             tree : this.tree,
13846             target: targetNode,
13847             data: data,
13848             point: point,
13849             source: dd,
13850             rawEvent: e,
13851             dropNode: dropNode,
13852             cancel: !dropNode   
13853         };
13854         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13855         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13856             targetNode.ui.endDrop();
13857             return false;
13858         }
13859         // allow target changing
13860         targetNode = dropEvent.target;
13861         if(point == "append" && !targetNode.isExpanded()){
13862             targetNode.expand(false, null, function(){
13863                 this.completeDrop(dropEvent);
13864             }.createDelegate(this));
13865         }else{
13866             this.completeDrop(dropEvent);
13867         }
13868         return true;
13869     },
13870     
13871     completeDrop : function(de){
13872         var ns = de.dropNode, p = de.point, t = de.target;
13873         if(!(ns instanceof Array)){
13874             ns = [ns];
13875         }
13876         var n;
13877         for(var i = 0, len = ns.length; i < len; i++){
13878             n = ns[i];
13879             if(p == "above"){
13880                 t.parentNode.insertBefore(n, t);
13881             }else if(p == "below"){
13882                 t.parentNode.insertBefore(n, t.nextSibling);
13883             }else{
13884                 t.appendChild(n);
13885             }
13886         }
13887         n.ui.focus();
13888         if(this.tree.hlDrop){
13889             n.ui.highlight();
13890         }
13891         t.ui.endDrop();
13892         this.tree.fireEvent("nodedrop", de);
13893     },
13894     
13895     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13896         if(this.tree.hlDrop){
13897             dropNode.ui.focus();
13898             dropNode.ui.highlight();
13899         }
13900         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13901     },
13902     
13903     getTree : function(){
13904         return this.tree;
13905     },
13906     
13907     removeDropIndicators : function(n){
13908         if(n && n.ddel){
13909             var el = n.ddel;
13910             Roo.fly(el).removeClass([
13911                     "x-tree-drag-insert-above",
13912                     "x-tree-drag-insert-below",
13913                     "x-tree-drag-append"]);
13914             this.lastInsertClass = "_noclass";
13915         }
13916     },
13917     
13918     beforeDragDrop : function(target, e, id){
13919         this.cancelExpand();
13920         return true;
13921     },
13922     
13923     afterRepair : function(data){
13924         if(data && Roo.enableFx){
13925             data.node.ui.highlight();
13926         }
13927         this.hideProxy();
13928     } 
13929     
13930 });
13931
13932 }
13933 /*
13934  * Based on:
13935  * Ext JS Library 1.1.1
13936  * Copyright(c) 2006-2007, Ext JS, LLC.
13937  *
13938  * Originally Released Under LGPL - original licence link has changed is not relivant.
13939  *
13940  * Fork - LGPL
13941  * <script type="text/javascript">
13942  */
13943  
13944
13945 if(Roo.dd.DragZone){
13946 Roo.tree.TreeDragZone = function(tree, config){
13947     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13948     this.tree = tree;
13949 };
13950
13951 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13952     ddGroup : "TreeDD",
13953    
13954     onBeforeDrag : function(data, e){
13955         var n = data.node;
13956         return n && n.draggable && !n.disabled;
13957     },
13958      
13959     
13960     onInitDrag : function(e){
13961         var data = this.dragData;
13962         this.tree.getSelectionModel().select(data.node);
13963         this.proxy.update("");
13964         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13965         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13966     },
13967     
13968     getRepairXY : function(e, data){
13969         return data.node.ui.getDDRepairXY();
13970     },
13971     
13972     onEndDrag : function(data, e){
13973         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13974         
13975         
13976     },
13977     
13978     onValidDrop : function(dd, e, id){
13979         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13980         this.hideProxy();
13981     },
13982     
13983     beforeInvalidDrop : function(e, id){
13984         // this scrolls the original position back into view
13985         var sm = this.tree.getSelectionModel();
13986         sm.clearSelections();
13987         sm.select(this.dragData.node);
13988     }
13989 });
13990 }/*
13991  * Based on:
13992  * Ext JS Library 1.1.1
13993  * Copyright(c) 2006-2007, Ext JS, LLC.
13994  *
13995  * Originally Released Under LGPL - original licence link has changed is not relivant.
13996  *
13997  * Fork - LGPL
13998  * <script type="text/javascript">
13999  */
14000 /**
14001  * @class Roo.tree.TreeEditor
14002  * @extends Roo.Editor
14003  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14004  * as the editor field.
14005  * @constructor
14006  * @param {Object} config (used to be the tree panel.)
14007  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14008  * 
14009  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14010  * @cfg {Roo.form.TextField} field [required] The field configuration
14011  *
14012  * 
14013  */
14014 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14015     var tree = config;
14016     var field;
14017     if (oldconfig) { // old style..
14018         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14019     } else {
14020         // new style..
14021         tree = config.tree;
14022         config.field = config.field  || {};
14023         config.field.xtype = 'TextField';
14024         field = Roo.factory(config.field, Roo.form);
14025     }
14026     config = config || {};
14027     
14028     
14029     this.addEvents({
14030         /**
14031          * @event beforenodeedit
14032          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14033          * false from the handler of this event.
14034          * @param {Editor} this
14035          * @param {Roo.tree.Node} node 
14036          */
14037         "beforenodeedit" : true
14038     });
14039     
14040     //Roo.log(config);
14041     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14042
14043     this.tree = tree;
14044
14045     tree.on('beforeclick', this.beforeNodeClick, this);
14046     tree.getTreeEl().on('mousedown', this.hide, this);
14047     this.on('complete', this.updateNode, this);
14048     this.on('beforestartedit', this.fitToTree, this);
14049     this.on('startedit', this.bindScroll, this, {delay:10});
14050     this.on('specialkey', this.onSpecialKey, this);
14051 };
14052
14053 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14054     /**
14055      * @cfg {String} alignment
14056      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14057      */
14058     alignment: "l-l",
14059     // inherit
14060     autoSize: false,
14061     /**
14062      * @cfg {Boolean} hideEl
14063      * True to hide the bound element while the editor is displayed (defaults to false)
14064      */
14065     hideEl : false,
14066     /**
14067      * @cfg {String} cls
14068      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14069      */
14070     cls: "x-small-editor x-tree-editor",
14071     /**
14072      * @cfg {Boolean} shim
14073      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14074      */
14075     shim:false,
14076     // inherit
14077     shadow:"frame",
14078     /**
14079      * @cfg {Number} maxWidth
14080      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14081      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14082      * scroll and client offsets into account prior to each edit.
14083      */
14084     maxWidth: 250,
14085
14086     editDelay : 350,
14087
14088     // private
14089     fitToTree : function(ed, el){
14090         var td = this.tree.getTreeEl().dom, nd = el.dom;
14091         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14092             td.scrollLeft = nd.offsetLeft;
14093         }
14094         var w = Math.min(
14095                 this.maxWidth,
14096                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14097         this.setSize(w, '');
14098         
14099         return this.fireEvent('beforenodeedit', this, this.editNode);
14100         
14101     },
14102
14103     // private
14104     triggerEdit : function(node){
14105         this.completeEdit();
14106         this.editNode = node;
14107         this.startEdit(node.ui.textNode, node.text);
14108     },
14109
14110     // private
14111     bindScroll : function(){
14112         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14113     },
14114
14115     // private
14116     beforeNodeClick : function(node, e){
14117         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14118         this.lastClick = new Date();
14119         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14120             e.stopEvent();
14121             this.triggerEdit(node);
14122             return false;
14123         }
14124         return true;
14125     },
14126
14127     // private
14128     updateNode : function(ed, value){
14129         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14130         this.editNode.setText(value);
14131     },
14132
14133     // private
14134     onHide : function(){
14135         Roo.tree.TreeEditor.superclass.onHide.call(this);
14136         if(this.editNode){
14137             this.editNode.ui.focus();
14138         }
14139     },
14140
14141     // private
14142     onSpecialKey : function(field, e){
14143         var k = e.getKey();
14144         if(k == e.ESC){
14145             e.stopEvent();
14146             this.cancelEdit();
14147         }else if(k == e.ENTER && !e.hasModifier()){
14148             e.stopEvent();
14149             this.completeEdit();
14150         }
14151     }
14152 });//<Script type="text/javascript">
14153 /*
14154  * Based on:
14155  * Ext JS Library 1.1.1
14156  * Copyright(c) 2006-2007, Ext JS, LLC.
14157  *
14158  * Originally Released Under LGPL - original licence link has changed is not relivant.
14159  *
14160  * Fork - LGPL
14161  * <script type="text/javascript">
14162  */
14163  
14164 /**
14165  * Not documented??? - probably should be...
14166  */
14167
14168 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14169     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14170     
14171     renderElements : function(n, a, targetNode, bulkRender){
14172         //consel.log("renderElements?");
14173         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14174
14175         var t = n.getOwnerTree();
14176         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14177         
14178         var cols = t.columns;
14179         var bw = t.borderWidth;
14180         var c = cols[0];
14181         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14182          var cb = typeof a.checked == "boolean";
14183         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14184         var colcls = 'x-t-' + tid + '-c0';
14185         var buf = [
14186             '<li class="x-tree-node">',
14187             
14188                 
14189                 '<div class="x-tree-node-el ', a.cls,'">',
14190                     // extran...
14191                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14192                 
14193                 
14194                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14195                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14196                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14197                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14198                            (a.iconCls ? ' '+a.iconCls : ''),
14199                            '" unselectable="on" />',
14200                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14201                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14202                              
14203                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14204                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14205                             '<span unselectable="on" qtip="' + tx + '">',
14206                              tx,
14207                              '</span></a>' ,
14208                     '</div>',
14209                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14210                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14211                  ];
14212         for(var i = 1, len = cols.length; i < len; i++){
14213             c = cols[i];
14214             colcls = 'x-t-' + tid + '-c' +i;
14215             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14216             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14217                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14218                       "</div>");
14219          }
14220          
14221          buf.push(
14222             '</a>',
14223             '<div class="x-clear"></div></div>',
14224             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14225             "</li>");
14226         
14227         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14228             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14229                                 n.nextSibling.ui.getEl(), buf.join(""));
14230         }else{
14231             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14232         }
14233         var el = this.wrap.firstChild;
14234         this.elRow = el;
14235         this.elNode = el.firstChild;
14236         this.ranchor = el.childNodes[1];
14237         this.ctNode = this.wrap.childNodes[1];
14238         var cs = el.firstChild.childNodes;
14239         this.indentNode = cs[0];
14240         this.ecNode = cs[1];
14241         this.iconNode = cs[2];
14242         var index = 3;
14243         if(cb){
14244             this.checkbox = cs[3];
14245             index++;
14246         }
14247         this.anchor = cs[index];
14248         
14249         this.textNode = cs[index].firstChild;
14250         
14251         //el.on("click", this.onClick, this);
14252         //el.on("dblclick", this.onDblClick, this);
14253         
14254         
14255        // console.log(this);
14256     },
14257     initEvents : function(){
14258         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14259         
14260             
14261         var a = this.ranchor;
14262
14263         var el = Roo.get(a);
14264
14265         if(Roo.isOpera){ // opera render bug ignores the CSS
14266             el.setStyle("text-decoration", "none");
14267         }
14268
14269         el.on("click", this.onClick, this);
14270         el.on("dblclick", this.onDblClick, this);
14271         el.on("contextmenu", this.onContextMenu, this);
14272         
14273     },
14274     
14275     /*onSelectedChange : function(state){
14276         if(state){
14277             this.focus();
14278             this.addClass("x-tree-selected");
14279         }else{
14280             //this.blur();
14281             this.removeClass("x-tree-selected");
14282         }
14283     },*/
14284     addClass : function(cls){
14285         if(this.elRow){
14286             Roo.fly(this.elRow).addClass(cls);
14287         }
14288         
14289     },
14290     
14291     
14292     removeClass : function(cls){
14293         if(this.elRow){
14294             Roo.fly(this.elRow).removeClass(cls);
14295         }
14296     }
14297
14298     
14299     
14300 });//<Script type="text/javascript">
14301
14302 /*
14303  * Based on:
14304  * Ext JS Library 1.1.1
14305  * Copyright(c) 2006-2007, Ext JS, LLC.
14306  *
14307  * Originally Released Under LGPL - original licence link has changed is not relivant.
14308  *
14309  * Fork - LGPL
14310  * <script type="text/javascript">
14311  */
14312  
14313
14314 /**
14315  * @class Roo.tree.ColumnTree
14316  * @extends Roo.tree.TreePanel
14317  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14318  * @cfg {int} borderWidth  compined right/left border allowance
14319  * @constructor
14320  * @param {String/HTMLElement/Element} el The container element
14321  * @param {Object} config
14322  */
14323 Roo.tree.ColumnTree =  function(el, config)
14324 {
14325    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14326    this.addEvents({
14327         /**
14328         * @event resize
14329         * Fire this event on a container when it resizes
14330         * @param {int} w Width
14331         * @param {int} h Height
14332         */
14333        "resize" : true
14334     });
14335     this.on('resize', this.onResize, this);
14336 };
14337
14338 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14339     //lines:false,
14340     
14341     
14342     borderWidth: Roo.isBorderBox ? 0 : 2, 
14343     headEls : false,
14344     
14345     render : function(){
14346         // add the header.....
14347        
14348         Roo.tree.ColumnTree.superclass.render.apply(this);
14349         
14350         this.el.addClass('x-column-tree');
14351         
14352         this.headers = this.el.createChild(
14353             {cls:'x-tree-headers'},this.innerCt.dom);
14354    
14355         var cols = this.columns, c;
14356         var totalWidth = 0;
14357         this.headEls = [];
14358         var  len = cols.length;
14359         for(var i = 0; i < len; i++){
14360              c = cols[i];
14361              totalWidth += c.width;
14362             this.headEls.push(this.headers.createChild({
14363                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14364                  cn: {
14365                      cls:'x-tree-hd-text',
14366                      html: c.header
14367                  },
14368                  style:'width:'+(c.width-this.borderWidth)+'px;'
14369              }));
14370         }
14371         this.headers.createChild({cls:'x-clear'});
14372         // prevent floats from wrapping when clipped
14373         this.headers.setWidth(totalWidth);
14374         //this.innerCt.setWidth(totalWidth);
14375         this.innerCt.setStyle({ overflow: 'auto' });
14376         this.onResize(this.width, this.height);
14377              
14378         
14379     },
14380     onResize : function(w,h)
14381     {
14382         this.height = h;
14383         this.width = w;
14384         // resize cols..
14385         this.innerCt.setWidth(this.width);
14386         this.innerCt.setHeight(this.height-20);
14387         
14388         // headers...
14389         var cols = this.columns, c;
14390         var totalWidth = 0;
14391         var expEl = false;
14392         var len = cols.length;
14393         for(var i = 0; i < len; i++){
14394             c = cols[i];
14395             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14396                 // it's the expander..
14397                 expEl  = this.headEls[i];
14398                 continue;
14399             }
14400             totalWidth += c.width;
14401             
14402         }
14403         if (expEl) {
14404             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14405         }
14406         this.headers.setWidth(w-20);
14407
14408         
14409         
14410         
14411     }
14412 });
14413 /*
14414  * Based on:
14415  * Ext JS Library 1.1.1
14416  * Copyright(c) 2006-2007, Ext JS, LLC.
14417  *
14418  * Originally Released Under LGPL - original licence link has changed is not relivant.
14419  *
14420  * Fork - LGPL
14421  * <script type="text/javascript">
14422  */
14423  
14424 /**
14425  * @class Roo.menu.Menu
14426  * @extends Roo.util.Observable
14427  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14428  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14429  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14430  * @constructor
14431  * Creates a new Menu
14432  * @param {Object} config Configuration options
14433  */
14434 Roo.menu.Menu = function(config){
14435     
14436     Roo.menu.Menu.superclass.constructor.call(this, config);
14437     
14438     this.id = this.id || Roo.id();
14439     this.addEvents({
14440         /**
14441          * @event beforeshow
14442          * Fires before this menu is displayed
14443          * @param {Roo.menu.Menu} this
14444          */
14445         beforeshow : true,
14446         /**
14447          * @event beforehide
14448          * Fires before this menu is hidden
14449          * @param {Roo.menu.Menu} this
14450          */
14451         beforehide : true,
14452         /**
14453          * @event show
14454          * Fires after this menu is displayed
14455          * @param {Roo.menu.Menu} this
14456          */
14457         show : true,
14458         /**
14459          * @event hide
14460          * Fires after this menu is hidden
14461          * @param {Roo.menu.Menu} this
14462          */
14463         hide : true,
14464         /**
14465          * @event click
14466          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14467          * @param {Roo.menu.Menu} this
14468          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14469          * @param {Roo.EventObject} e
14470          */
14471         click : true,
14472         /**
14473          * @event mouseover
14474          * Fires when the mouse is hovering over this menu
14475          * @param {Roo.menu.Menu} this
14476          * @param {Roo.EventObject} e
14477          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14478          */
14479         mouseover : true,
14480         /**
14481          * @event mouseout
14482          * Fires when the mouse exits this menu
14483          * @param {Roo.menu.Menu} this
14484          * @param {Roo.EventObject} e
14485          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14486          */
14487         mouseout : true,
14488         /**
14489          * @event itemclick
14490          * Fires when a menu item contained in this menu is clicked
14491          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14492          * @param {Roo.EventObject} e
14493          */
14494         itemclick: true
14495     });
14496     if (this.registerMenu) {
14497         Roo.menu.MenuMgr.register(this);
14498     }
14499     
14500     var mis = this.items;
14501     this.items = new Roo.util.MixedCollection();
14502     if(mis){
14503         this.add.apply(this, mis);
14504     }
14505 };
14506
14507 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14508     /**
14509      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14510      */
14511     minWidth : 120,
14512     /**
14513      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14514      * for bottom-right shadow (defaults to "sides")
14515      */
14516     shadow : "sides",
14517     /**
14518      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14519      * this menu (defaults to "tl-tr?")
14520      */
14521     subMenuAlign : "tl-tr?",
14522     /**
14523      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14524      * relative to its element of origin (defaults to "tl-bl?")
14525      */
14526     defaultAlign : "tl-bl?",
14527     /**
14528      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14529      */
14530     allowOtherMenus : false,
14531     /**
14532      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14533      */
14534     registerMenu : true,
14535
14536     hidden:true,
14537
14538     // private
14539     render : function(){
14540         if(this.el){
14541             return;
14542         }
14543         var el = this.el = new Roo.Layer({
14544             cls: "x-menu",
14545             shadow:this.shadow,
14546             constrain: false,
14547             parentEl: this.parentEl || document.body,
14548             zindex:15000
14549         });
14550
14551         this.keyNav = new Roo.menu.MenuNav(this);
14552
14553         if(this.plain){
14554             el.addClass("x-menu-plain");
14555         }
14556         if(this.cls){
14557             el.addClass(this.cls);
14558         }
14559         // generic focus element
14560         this.focusEl = el.createChild({
14561             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14562         });
14563         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14564         //disabling touch- as it's causing issues ..
14565         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14566         ul.on('click'   , this.onClick, this);
14567         
14568         
14569         ul.on("mouseover", this.onMouseOver, this);
14570         ul.on("mouseout", this.onMouseOut, this);
14571         this.items.each(function(item){
14572             if (item.hidden) {
14573                 return;
14574             }
14575             
14576             var li = document.createElement("li");
14577             li.className = "x-menu-list-item";
14578             ul.dom.appendChild(li);
14579             item.render(li, this);
14580         }, this);
14581         this.ul = ul;
14582         this.autoWidth();
14583     },
14584
14585     // private
14586     autoWidth : function(){
14587         var el = this.el, ul = this.ul;
14588         if(!el){
14589             return;
14590         }
14591         var w = this.width;
14592         if(w){
14593             el.setWidth(w);
14594         }else if(Roo.isIE){
14595             el.setWidth(this.minWidth);
14596             var t = el.dom.offsetWidth; // force recalc
14597             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14598         }
14599     },
14600
14601     // private
14602     delayAutoWidth : function(){
14603         if(this.rendered){
14604             if(!this.awTask){
14605                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14606             }
14607             this.awTask.delay(20);
14608         }
14609     },
14610
14611     // private
14612     findTargetItem : function(e){
14613         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14614         if(t && t.menuItemId){
14615             return this.items.get(t.menuItemId);
14616         }
14617     },
14618
14619     // private
14620     onClick : function(e){
14621         Roo.log("menu.onClick");
14622         var t = this.findTargetItem(e);
14623         if(!t){
14624             return;
14625         }
14626         Roo.log(e);
14627         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14628             if(t == this.activeItem && t.shouldDeactivate(e)){
14629                 this.activeItem.deactivate();
14630                 delete this.activeItem;
14631                 return;
14632             }
14633             if(t.canActivate){
14634                 this.setActiveItem(t, true);
14635             }
14636             return;
14637             
14638             
14639         }
14640         
14641         t.onClick(e);
14642         this.fireEvent("click", this, t, e);
14643     },
14644
14645     // private
14646     setActiveItem : function(item, autoExpand){
14647         if(item != this.activeItem){
14648             if(this.activeItem){
14649                 this.activeItem.deactivate();
14650             }
14651             this.activeItem = item;
14652             item.activate(autoExpand);
14653         }else if(autoExpand){
14654             item.expandMenu();
14655         }
14656     },
14657
14658     // private
14659     tryActivate : function(start, step){
14660         var items = this.items;
14661         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14662             var item = items.get(i);
14663             if(!item.disabled && item.canActivate){
14664                 this.setActiveItem(item, false);
14665                 return item;
14666             }
14667         }
14668         return false;
14669     },
14670
14671     // private
14672     onMouseOver : function(e){
14673         var t;
14674         if(t = this.findTargetItem(e)){
14675             if(t.canActivate && !t.disabled){
14676                 this.setActiveItem(t, true);
14677             }
14678         }
14679         this.fireEvent("mouseover", this, e, t);
14680     },
14681
14682     // private
14683     onMouseOut : function(e){
14684         var t;
14685         if(t = this.findTargetItem(e)){
14686             if(t == this.activeItem && t.shouldDeactivate(e)){
14687                 this.activeItem.deactivate();
14688                 delete this.activeItem;
14689             }
14690         }
14691         this.fireEvent("mouseout", this, e, t);
14692     },
14693
14694     /**
14695      * Read-only.  Returns true if the menu is currently displayed, else false.
14696      * @type Boolean
14697      */
14698     isVisible : function(){
14699         return this.el && !this.hidden;
14700     },
14701
14702     /**
14703      * Displays this menu relative to another element
14704      * @param {String/HTMLElement/Roo.Element} element The element to align to
14705      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14706      * the element (defaults to this.defaultAlign)
14707      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14708      */
14709     show : function(el, pos, parentMenu){
14710         this.parentMenu = parentMenu;
14711         if(!this.el){
14712             this.render();
14713         }
14714         this.fireEvent("beforeshow", this);
14715         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14716     },
14717
14718     /**
14719      * Displays this menu at a specific xy position
14720      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14721      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14722      */
14723     showAt : function(xy, parentMenu, /* private: */_e){
14724         this.parentMenu = parentMenu;
14725         if(!this.el){
14726             this.render();
14727         }
14728         if(_e !== false){
14729             this.fireEvent("beforeshow", this);
14730             xy = this.el.adjustForConstraints(xy);
14731         }
14732         this.el.setXY(xy);
14733         this.el.show();
14734         this.hidden = false;
14735         this.focus();
14736         this.fireEvent("show", this);
14737     },
14738
14739     focus : function(){
14740         if(!this.hidden){
14741             this.doFocus.defer(50, this);
14742         }
14743     },
14744
14745     doFocus : function(){
14746         if(!this.hidden){
14747             this.focusEl.focus();
14748         }
14749     },
14750
14751     /**
14752      * Hides this menu and optionally all parent menus
14753      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14754      */
14755     hide : function(deep){
14756         if(this.el && this.isVisible()){
14757             this.fireEvent("beforehide", this);
14758             if(this.activeItem){
14759                 this.activeItem.deactivate();
14760                 this.activeItem = null;
14761             }
14762             this.el.hide();
14763             this.hidden = true;
14764             this.fireEvent("hide", this);
14765         }
14766         if(deep === true && this.parentMenu){
14767             this.parentMenu.hide(true);
14768         }
14769     },
14770
14771     /**
14772      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14773      * Any of the following are valid:
14774      * <ul>
14775      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14776      * <li>An HTMLElement object which will be converted to a menu item</li>
14777      * <li>A menu item config object that will be created as a new menu item</li>
14778      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14779      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14780      * </ul>
14781      * Usage:
14782      * <pre><code>
14783 // Create the menu
14784 var menu = new Roo.menu.Menu();
14785
14786 // Create a menu item to add by reference
14787 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14788
14789 // Add a bunch of items at once using different methods.
14790 // Only the last item added will be returned.
14791 var item = menu.add(
14792     menuItem,                // add existing item by ref
14793     'Dynamic Item',          // new TextItem
14794     '-',                     // new separator
14795     { text: 'Config Item' }  // new item by config
14796 );
14797 </code></pre>
14798      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14799      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14800      */
14801     add : function(){
14802         var a = arguments, l = a.length, item;
14803         for(var i = 0; i < l; i++){
14804             var el = a[i];
14805             if ((typeof(el) == "object") && el.xtype && el.xns) {
14806                 el = Roo.factory(el, Roo.menu);
14807             }
14808             
14809             if(el.render){ // some kind of Item
14810                 item = this.addItem(el);
14811             }else if(typeof el == "string"){ // string
14812                 if(el == "separator" || el == "-"){
14813                     item = this.addSeparator();
14814                 }else{
14815                     item = this.addText(el);
14816                 }
14817             }else if(el.tagName || el.el){ // element
14818                 item = this.addElement(el);
14819             }else if(typeof el == "object"){ // must be menu item config?
14820                 item = this.addMenuItem(el);
14821             }
14822         }
14823         return item;
14824     },
14825
14826     /**
14827      * Returns this menu's underlying {@link Roo.Element} object
14828      * @return {Roo.Element} The element
14829      */
14830     getEl : function(){
14831         if(!this.el){
14832             this.render();
14833         }
14834         return this.el;
14835     },
14836
14837     /**
14838      * Adds a separator bar to the menu
14839      * @return {Roo.menu.Item} The menu item that was added
14840      */
14841     addSeparator : function(){
14842         return this.addItem(new Roo.menu.Separator());
14843     },
14844
14845     /**
14846      * Adds an {@link Roo.Element} object to the menu
14847      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14848      * @return {Roo.menu.Item} The menu item that was added
14849      */
14850     addElement : function(el){
14851         return this.addItem(new Roo.menu.BaseItem(el));
14852     },
14853
14854     /**
14855      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14856      * @param {Roo.menu.Item} item The menu item to add
14857      * @return {Roo.menu.Item} The menu item that was added
14858      */
14859     addItem : function(item){
14860         this.items.add(item);
14861         if(this.ul){
14862             var li = document.createElement("li");
14863             li.className = "x-menu-list-item";
14864             this.ul.dom.appendChild(li);
14865             item.render(li, this);
14866             this.delayAutoWidth();
14867         }
14868         return item;
14869     },
14870
14871     /**
14872      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14873      * @param {Object} config A MenuItem config object
14874      * @return {Roo.menu.Item} The menu item that was added
14875      */
14876     addMenuItem : function(config){
14877         if(!(config instanceof Roo.menu.Item)){
14878             if(typeof config.checked == "boolean"){ // must be check menu item config?
14879                 config = new Roo.menu.CheckItem(config);
14880             }else{
14881                 config = new Roo.menu.Item(config);
14882             }
14883         }
14884         return this.addItem(config);
14885     },
14886
14887     /**
14888      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14889      * @param {String} text The text to display in the menu item
14890      * @return {Roo.menu.Item} The menu item that was added
14891      */
14892     addText : function(text){
14893         return this.addItem(new Roo.menu.TextItem({ text : text }));
14894     },
14895
14896     /**
14897      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14898      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14899      * @param {Roo.menu.Item} item The menu item to add
14900      * @return {Roo.menu.Item} The menu item that was added
14901      */
14902     insert : function(index, item){
14903         this.items.insert(index, item);
14904         if(this.ul){
14905             var li = document.createElement("li");
14906             li.className = "x-menu-list-item";
14907             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14908             item.render(li, this);
14909             this.delayAutoWidth();
14910         }
14911         return item;
14912     },
14913
14914     /**
14915      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14916      * @param {Roo.menu.Item} item The menu item to remove
14917      */
14918     remove : function(item){
14919         this.items.removeKey(item.id);
14920         item.destroy();
14921     },
14922
14923     /**
14924      * Removes and destroys all items in the menu
14925      */
14926     removeAll : function(){
14927         var f;
14928         while(f = this.items.first()){
14929             this.remove(f);
14930         }
14931     }
14932 });
14933
14934 // MenuNav is a private utility class used internally by the Menu
14935 Roo.menu.MenuNav = function(menu){
14936     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14937     this.scope = this.menu = menu;
14938 };
14939
14940 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14941     doRelay : function(e, h){
14942         var k = e.getKey();
14943         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14944             this.menu.tryActivate(0, 1);
14945             return false;
14946         }
14947         return h.call(this.scope || this, e, this.menu);
14948     },
14949
14950     up : function(e, m){
14951         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14952             m.tryActivate(m.items.length-1, -1);
14953         }
14954     },
14955
14956     down : function(e, m){
14957         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14958             m.tryActivate(0, 1);
14959         }
14960     },
14961
14962     right : function(e, m){
14963         if(m.activeItem){
14964             m.activeItem.expandMenu(true);
14965         }
14966     },
14967
14968     left : function(e, m){
14969         m.hide();
14970         if(m.parentMenu && m.parentMenu.activeItem){
14971             m.parentMenu.activeItem.activate();
14972         }
14973     },
14974
14975     enter : function(e, m){
14976         if(m.activeItem){
14977             e.stopPropagation();
14978             m.activeItem.onClick(e);
14979             m.fireEvent("click", this, m.activeItem);
14980             return true;
14981         }
14982     }
14983 });/*
14984  * Based on:
14985  * Ext JS Library 1.1.1
14986  * Copyright(c) 2006-2007, Ext JS, LLC.
14987  *
14988  * Originally Released Under LGPL - original licence link has changed is not relivant.
14989  *
14990  * Fork - LGPL
14991  * <script type="text/javascript">
14992  */
14993  
14994 /**
14995  * @class Roo.menu.MenuMgr
14996  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14997  * @static
14998  */
14999 Roo.menu.MenuMgr = function(){
15000    var menus, active, groups = {}, attached = false, lastShow = new Date();
15001
15002    // private - called when first menu is created
15003    function init(){
15004        menus = {};
15005        active = new Roo.util.MixedCollection();
15006        Roo.get(document).addKeyListener(27, function(){
15007            if(active.length > 0){
15008                hideAll();
15009            }
15010        });
15011    }
15012
15013    // private
15014    function hideAll(){
15015        if(active && active.length > 0){
15016            var c = active.clone();
15017            c.each(function(m){
15018                m.hide();
15019            });
15020        }
15021    }
15022
15023    // private
15024    function onHide(m){
15025        active.remove(m);
15026        if(active.length < 1){
15027            Roo.get(document).un("mousedown", onMouseDown);
15028            attached = false;
15029        }
15030    }
15031
15032    // private
15033    function onShow(m){
15034        var last = active.last();
15035        lastShow = new Date();
15036        active.add(m);
15037        if(!attached){
15038            Roo.get(document).on("mousedown", onMouseDown);
15039            attached = true;
15040        }
15041        if(m.parentMenu){
15042           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15043           m.parentMenu.activeChild = m;
15044        }else if(last && last.isVisible()){
15045           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15046        }
15047    }
15048
15049    // private
15050    function onBeforeHide(m){
15051        if(m.activeChild){
15052            m.activeChild.hide();
15053        }
15054        if(m.autoHideTimer){
15055            clearTimeout(m.autoHideTimer);
15056            delete m.autoHideTimer;
15057        }
15058    }
15059
15060    // private
15061    function onBeforeShow(m){
15062        var pm = m.parentMenu;
15063        if(!pm && !m.allowOtherMenus){
15064            hideAll();
15065        }else if(pm && pm.activeChild && active != m){
15066            pm.activeChild.hide();
15067        }
15068    }
15069
15070    // private
15071    function onMouseDown(e){
15072        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15073            hideAll();
15074        }
15075    }
15076
15077    // private
15078    function onBeforeCheck(mi, state){
15079        if(state){
15080            var g = groups[mi.group];
15081            for(var i = 0, l = g.length; i < l; i++){
15082                if(g[i] != mi){
15083                    g[i].setChecked(false);
15084                }
15085            }
15086        }
15087    }
15088
15089    return {
15090
15091        /**
15092         * Hides all menus that are currently visible
15093         */
15094        hideAll : function(){
15095             hideAll();  
15096        },
15097
15098        // private
15099        register : function(menu){
15100            if(!menus){
15101                init();
15102            }
15103            menus[menu.id] = menu;
15104            menu.on("beforehide", onBeforeHide);
15105            menu.on("hide", onHide);
15106            menu.on("beforeshow", onBeforeShow);
15107            menu.on("show", onShow);
15108            var g = menu.group;
15109            if(g && menu.events["checkchange"]){
15110                if(!groups[g]){
15111                    groups[g] = [];
15112                }
15113                groups[g].push(menu);
15114                menu.on("checkchange", onCheck);
15115            }
15116        },
15117
15118         /**
15119          * Returns a {@link Roo.menu.Menu} object
15120          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15121          * be used to generate and return a new Menu instance.
15122          */
15123        get : function(menu){
15124            if(typeof menu == "string"){ // menu id
15125                return menus[menu];
15126            }else if(menu.events){  // menu instance
15127                return menu;
15128            }else if(typeof menu.length == 'number'){ // array of menu items?
15129                return new Roo.menu.Menu({items:menu});
15130            }else{ // otherwise, must be a config
15131                return new Roo.menu.Menu(menu);
15132            }
15133        },
15134
15135        // private
15136        unregister : function(menu){
15137            delete menus[menu.id];
15138            menu.un("beforehide", onBeforeHide);
15139            menu.un("hide", onHide);
15140            menu.un("beforeshow", onBeforeShow);
15141            menu.un("show", onShow);
15142            var g = menu.group;
15143            if(g && menu.events["checkchange"]){
15144                groups[g].remove(menu);
15145                menu.un("checkchange", onCheck);
15146            }
15147        },
15148
15149        // private
15150        registerCheckable : function(menuItem){
15151            var g = menuItem.group;
15152            if(g){
15153                if(!groups[g]){
15154                    groups[g] = [];
15155                }
15156                groups[g].push(menuItem);
15157                menuItem.on("beforecheckchange", onBeforeCheck);
15158            }
15159        },
15160
15161        // private
15162        unregisterCheckable : function(menuItem){
15163            var g = menuItem.group;
15164            if(g){
15165                groups[g].remove(menuItem);
15166                menuItem.un("beforecheckchange", onBeforeCheck);
15167            }
15168        }
15169    };
15170 }();/*
15171  * Based on:
15172  * Ext JS Library 1.1.1
15173  * Copyright(c) 2006-2007, Ext JS, LLC.
15174  *
15175  * Originally Released Under LGPL - original licence link has changed is not relivant.
15176  *
15177  * Fork - LGPL
15178  * <script type="text/javascript">
15179  */
15180  
15181
15182 /**
15183  * @class Roo.menu.BaseItem
15184  * @extends Roo.Component
15185  * @abstract
15186  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15187  * management and base configuration options shared by all menu components.
15188  * @constructor
15189  * Creates a new BaseItem
15190  * @param {Object} config Configuration options
15191  */
15192 Roo.menu.BaseItem = function(config){
15193     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15194
15195     this.addEvents({
15196         /**
15197          * @event click
15198          * Fires when this item is clicked
15199          * @param {Roo.menu.BaseItem} this
15200          * @param {Roo.EventObject} e
15201          */
15202         click: true,
15203         /**
15204          * @event activate
15205          * Fires when this item is activated
15206          * @param {Roo.menu.BaseItem} this
15207          */
15208         activate : true,
15209         /**
15210          * @event deactivate
15211          * Fires when this item is deactivated
15212          * @param {Roo.menu.BaseItem} this
15213          */
15214         deactivate : true
15215     });
15216
15217     if(this.handler){
15218         this.on("click", this.handler, this.scope, true);
15219     }
15220 };
15221
15222 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15223     /**
15224      * @cfg {Function} handler
15225      * A function that will handle the click event of this menu item (defaults to undefined)
15226      */
15227     /**
15228      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15229      */
15230     canActivate : false,
15231     
15232      /**
15233      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15234      */
15235     hidden: false,
15236     
15237     /**
15238      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15239      */
15240     activeClass : "x-menu-item-active",
15241     /**
15242      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15243      */
15244     hideOnClick : true,
15245     /**
15246      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15247      */
15248     hideDelay : 100,
15249
15250     // private
15251     ctype: "Roo.menu.BaseItem",
15252
15253     // private
15254     actionMode : "container",
15255
15256     // private
15257     render : function(container, parentMenu){
15258         this.parentMenu = parentMenu;
15259         Roo.menu.BaseItem.superclass.render.call(this, container);
15260         this.container.menuItemId = this.id;
15261     },
15262
15263     // private
15264     onRender : function(container, position){
15265         this.el = Roo.get(this.el);
15266         container.dom.appendChild(this.el.dom);
15267     },
15268
15269     // private
15270     onClick : function(e){
15271         if(!this.disabled && this.fireEvent("click", this, e) !== false
15272                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15273             this.handleClick(e);
15274         }else{
15275             e.stopEvent();
15276         }
15277     },
15278
15279     // private
15280     activate : function(){
15281         if(this.disabled){
15282             return false;
15283         }
15284         var li = this.container;
15285         li.addClass(this.activeClass);
15286         this.region = li.getRegion().adjust(2, 2, -2, -2);
15287         this.fireEvent("activate", this);
15288         return true;
15289     },
15290
15291     // private
15292     deactivate : function(){
15293         this.container.removeClass(this.activeClass);
15294         this.fireEvent("deactivate", this);
15295     },
15296
15297     // private
15298     shouldDeactivate : function(e){
15299         return !this.region || !this.region.contains(e.getPoint());
15300     },
15301
15302     // private
15303     handleClick : function(e){
15304         if(this.hideOnClick){
15305             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15306         }
15307     },
15308
15309     // private
15310     expandMenu : function(autoActivate){
15311         // do nothing
15312     },
15313
15314     // private
15315     hideMenu : function(){
15316         // do nothing
15317     }
15318 });/*
15319  * Based on:
15320  * Ext JS Library 1.1.1
15321  * Copyright(c) 2006-2007, Ext JS, LLC.
15322  *
15323  * Originally Released Under LGPL - original licence link has changed is not relivant.
15324  *
15325  * Fork - LGPL
15326  * <script type="text/javascript">
15327  */
15328  
15329 /**
15330  * @class Roo.menu.Adapter
15331  * @extends Roo.menu.BaseItem
15332  * @abstract
15333  * 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.
15334  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15335  * @constructor
15336  * Creates a new Adapter
15337  * @param {Object} config Configuration options
15338  */
15339 Roo.menu.Adapter = function(component, config){
15340     Roo.menu.Adapter.superclass.constructor.call(this, config);
15341     this.component = component;
15342 };
15343 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15344     // private
15345     canActivate : true,
15346
15347     // private
15348     onRender : function(container, position){
15349         this.component.render(container);
15350         this.el = this.component.getEl();
15351     },
15352
15353     // private
15354     activate : function(){
15355         if(this.disabled){
15356             return false;
15357         }
15358         this.component.focus();
15359         this.fireEvent("activate", this);
15360         return true;
15361     },
15362
15363     // private
15364     deactivate : function(){
15365         this.fireEvent("deactivate", this);
15366     },
15367
15368     // private
15369     disable : function(){
15370         this.component.disable();
15371         Roo.menu.Adapter.superclass.disable.call(this);
15372     },
15373
15374     // private
15375     enable : function(){
15376         this.component.enable();
15377         Roo.menu.Adapter.superclass.enable.call(this);
15378     }
15379 });/*
15380  * Based on:
15381  * Ext JS Library 1.1.1
15382  * Copyright(c) 2006-2007, Ext JS, LLC.
15383  *
15384  * Originally Released Under LGPL - original licence link has changed is not relivant.
15385  *
15386  * Fork - LGPL
15387  * <script type="text/javascript">
15388  */
15389
15390 /**
15391  * @class Roo.menu.TextItem
15392  * @extends Roo.menu.BaseItem
15393  * Adds a static text string to a menu, usually used as either a heading or group separator.
15394  * Note: old style constructor with text is still supported.
15395  * 
15396  * @constructor
15397  * Creates a new TextItem
15398  * @param {Object} cfg Configuration
15399  */
15400 Roo.menu.TextItem = function(cfg){
15401     if (typeof(cfg) == 'string') {
15402         this.text = cfg;
15403     } else {
15404         Roo.apply(this,cfg);
15405     }
15406     
15407     Roo.menu.TextItem.superclass.constructor.call(this);
15408 };
15409
15410 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15411     /**
15412      * @cfg {String} text Text to show on item.
15413      */
15414     text : '',
15415     
15416     /**
15417      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15418      */
15419     hideOnClick : false,
15420     /**
15421      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15422      */
15423     itemCls : "x-menu-text",
15424
15425     // private
15426     onRender : function(){
15427         var s = document.createElement("span");
15428         s.className = this.itemCls;
15429         s.innerHTML = this.text;
15430         this.el = s;
15431         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15432     }
15433 });/*
15434  * Based on:
15435  * Ext JS Library 1.1.1
15436  * Copyright(c) 2006-2007, Ext JS, LLC.
15437  *
15438  * Originally Released Under LGPL - original licence link has changed is not relivant.
15439  *
15440  * Fork - LGPL
15441  * <script type="text/javascript">
15442  */
15443
15444 /**
15445  * @class Roo.menu.Separator
15446  * @extends Roo.menu.BaseItem
15447  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15448  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15449  * @constructor
15450  * @param {Object} config Configuration options
15451  */
15452 Roo.menu.Separator = function(config){
15453     Roo.menu.Separator.superclass.constructor.call(this, config);
15454 };
15455
15456 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15457     /**
15458      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15459      */
15460     itemCls : "x-menu-sep",
15461     /**
15462      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15463      */
15464     hideOnClick : false,
15465
15466     // private
15467     onRender : function(li){
15468         var s = document.createElement("span");
15469         s.className = this.itemCls;
15470         s.innerHTML = "&#160;";
15471         this.el = s;
15472         li.addClass("x-menu-sep-li");
15473         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15474     }
15475 });/*
15476  * Based on:
15477  * Ext JS Library 1.1.1
15478  * Copyright(c) 2006-2007, Ext JS, LLC.
15479  *
15480  * Originally Released Under LGPL - original licence link has changed is not relivant.
15481  *
15482  * Fork - LGPL
15483  * <script type="text/javascript">
15484  */
15485 /**
15486  * @class Roo.menu.Item
15487  * @extends Roo.menu.BaseItem
15488  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15489  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15490  * activation and click handling.
15491  * @constructor
15492  * Creates a new Item
15493  * @param {Object} config Configuration options
15494  */
15495 Roo.menu.Item = function(config){
15496     Roo.menu.Item.superclass.constructor.call(this, config);
15497     if(this.menu){
15498         this.menu = Roo.menu.MenuMgr.get(this.menu);
15499     }
15500 };
15501 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15502     /**
15503      * @cfg {Roo.menu.Menu} menu
15504      * A Sub menu
15505      */
15506     /**
15507      * @cfg {String} text
15508      * The text to show on the menu item.
15509      */
15510     text: '',
15511      /**
15512      * @cfg {String} HTML to render in menu
15513      * The text to show on the menu item (HTML version).
15514      */
15515     html: '',
15516     /**
15517      * @cfg {String} icon
15518      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15519      */
15520     icon: undefined,
15521     /**
15522      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15523      */
15524     itemCls : "x-menu-item",
15525     /**
15526      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15527      */
15528     canActivate : true,
15529     /**
15530      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15531      */
15532     showDelay: 200,
15533     // doc'd in BaseItem
15534     hideDelay: 200,
15535
15536     // private
15537     ctype: "Roo.menu.Item",
15538     
15539     // private
15540     onRender : function(container, position){
15541         var el = document.createElement("a");
15542         el.hideFocus = true;
15543         el.unselectable = "on";
15544         el.href = this.href || "#";
15545         if(this.hrefTarget){
15546             el.target = this.hrefTarget;
15547         }
15548         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15549         
15550         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15551         
15552         el.innerHTML = String.format(
15553                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15554                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15555         this.el = el;
15556         Roo.menu.Item.superclass.onRender.call(this, container, position);
15557     },
15558
15559     /**
15560      * Sets the text to display in this menu item
15561      * @param {String} text The text to display
15562      * @param {Boolean} isHTML true to indicate text is pure html.
15563      */
15564     setText : function(text, isHTML){
15565         if (isHTML) {
15566             this.html = text;
15567         } else {
15568             this.text = text;
15569             this.html = '';
15570         }
15571         if(this.rendered){
15572             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15573      
15574             this.el.update(String.format(
15575                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15576                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15577             this.parentMenu.autoWidth();
15578         }
15579     },
15580
15581     // private
15582     handleClick : function(e){
15583         if(!this.href){ // if no link defined, stop the event automatically
15584             e.stopEvent();
15585         }
15586         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15587     },
15588
15589     // private
15590     activate : function(autoExpand){
15591         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15592             this.focus();
15593             if(autoExpand){
15594                 this.expandMenu();
15595             }
15596         }
15597         return true;
15598     },
15599
15600     // private
15601     shouldDeactivate : function(e){
15602         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15603             if(this.menu && this.menu.isVisible()){
15604                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15605             }
15606             return true;
15607         }
15608         return false;
15609     },
15610
15611     // private
15612     deactivate : function(){
15613         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15614         this.hideMenu();
15615     },
15616
15617     // private
15618     expandMenu : function(autoActivate){
15619         if(!this.disabled && this.menu){
15620             clearTimeout(this.hideTimer);
15621             delete this.hideTimer;
15622             if(!this.menu.isVisible() && !this.showTimer){
15623                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15624             }else if (this.menu.isVisible() && autoActivate){
15625                 this.menu.tryActivate(0, 1);
15626             }
15627         }
15628     },
15629
15630     // private
15631     deferExpand : function(autoActivate){
15632         delete this.showTimer;
15633         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15634         if(autoActivate){
15635             this.menu.tryActivate(0, 1);
15636         }
15637     },
15638
15639     // private
15640     hideMenu : function(){
15641         clearTimeout(this.showTimer);
15642         delete this.showTimer;
15643         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15644             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15645         }
15646     },
15647
15648     // private
15649     deferHide : function(){
15650         delete this.hideTimer;
15651         this.menu.hide();
15652     }
15653 });/*
15654  * Based on:
15655  * Ext JS Library 1.1.1
15656  * Copyright(c) 2006-2007, Ext JS, LLC.
15657  *
15658  * Originally Released Under LGPL - original licence link has changed is not relivant.
15659  *
15660  * Fork - LGPL
15661  * <script type="text/javascript">
15662  */
15663  
15664 /**
15665  * @class Roo.menu.CheckItem
15666  * @extends Roo.menu.Item
15667  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15668  * @constructor
15669  * Creates a new CheckItem
15670  * @param {Object} config Configuration options
15671  */
15672 Roo.menu.CheckItem = function(config){
15673     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15674     this.addEvents({
15675         /**
15676          * @event beforecheckchange
15677          * Fires before the checked value is set, providing an opportunity to cancel if needed
15678          * @param {Roo.menu.CheckItem} this
15679          * @param {Boolean} checked The new checked value that will be set
15680          */
15681         "beforecheckchange" : true,
15682         /**
15683          * @event checkchange
15684          * Fires after the checked value has been set
15685          * @param {Roo.menu.CheckItem} this
15686          * @param {Boolean} checked The checked value that was set
15687          */
15688         "checkchange" : true
15689     });
15690     if(this.checkHandler){
15691         this.on('checkchange', this.checkHandler, this.scope);
15692     }
15693 };
15694 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15695     /**
15696      * @cfg {String} group
15697      * All check items with the same group name will automatically be grouped into a single-select
15698      * radio button group (defaults to '')
15699      */
15700     /**
15701      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15702      */
15703     itemCls : "x-menu-item x-menu-check-item",
15704     /**
15705      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15706      */
15707     groupClass : "x-menu-group-item",
15708
15709     /**
15710      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15711      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15712      * initialized with checked = true will be rendered as checked.
15713      */
15714     checked: false,
15715
15716     // private
15717     ctype: "Roo.menu.CheckItem",
15718
15719     // private
15720     onRender : function(c){
15721         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15722         if(this.group){
15723             this.el.addClass(this.groupClass);
15724         }
15725         Roo.menu.MenuMgr.registerCheckable(this);
15726         if(this.checked){
15727             this.checked = false;
15728             this.setChecked(true, true);
15729         }
15730     },
15731
15732     // private
15733     destroy : function(){
15734         if(this.rendered){
15735             Roo.menu.MenuMgr.unregisterCheckable(this);
15736         }
15737         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15738     },
15739
15740     /**
15741      * Set the checked state of this item
15742      * @param {Boolean} checked The new checked value
15743      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15744      */
15745     setChecked : function(state, suppressEvent){
15746         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15747             if(this.container){
15748                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15749             }
15750             this.checked = state;
15751             if(suppressEvent !== true){
15752                 this.fireEvent("checkchange", this, state);
15753             }
15754         }
15755     },
15756
15757     // private
15758     handleClick : function(e){
15759        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15760            this.setChecked(!this.checked);
15761        }
15762        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15763     }
15764 });/*
15765  * Based on:
15766  * Ext JS Library 1.1.1
15767  * Copyright(c) 2006-2007, Ext JS, LLC.
15768  *
15769  * Originally Released Under LGPL - original licence link has changed is not relivant.
15770  *
15771  * Fork - LGPL
15772  * <script type="text/javascript">
15773  */
15774  
15775 /**
15776  * @class Roo.menu.DateItem
15777  * @extends Roo.menu.Adapter
15778  * A menu item that wraps the {@link Roo.DatPicker} component.
15779  * @constructor
15780  * Creates a new DateItem
15781  * @param {Object} config Configuration options
15782  */
15783 Roo.menu.DateItem = function(config){
15784     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15785     /** The Roo.DatePicker object @type Roo.DatePicker */
15786     this.picker = this.component;
15787     this.addEvents({select: true});
15788     
15789     this.picker.on("render", function(picker){
15790         picker.getEl().swallowEvent("click");
15791         picker.container.addClass("x-menu-date-item");
15792     });
15793
15794     this.picker.on("select", this.onSelect, this);
15795 };
15796
15797 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15798     // private
15799     onSelect : function(picker, date){
15800         this.fireEvent("select", this, date, picker);
15801         Roo.menu.DateItem.superclass.handleClick.call(this);
15802     }
15803 });/*
15804  * Based on:
15805  * Ext JS Library 1.1.1
15806  * Copyright(c) 2006-2007, Ext JS, LLC.
15807  *
15808  * Originally Released Under LGPL - original licence link has changed is not relivant.
15809  *
15810  * Fork - LGPL
15811  * <script type="text/javascript">
15812  */
15813  
15814 /**
15815  * @class Roo.menu.ColorItem
15816  * @extends Roo.menu.Adapter
15817  * A menu item that wraps the {@link Roo.ColorPalette} component.
15818  * @constructor
15819  * Creates a new ColorItem
15820  * @param {Object} config Configuration options
15821  */
15822 Roo.menu.ColorItem = function(config){
15823     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15824     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15825     this.palette = this.component;
15826     this.relayEvents(this.palette, ["select"]);
15827     if(this.selectHandler){
15828         this.on('select', this.selectHandler, this.scope);
15829     }
15830 };
15831 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841  
15842
15843 /**
15844  * @class Roo.menu.DateMenu
15845  * @extends Roo.menu.Menu
15846  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15847  * @constructor
15848  * Creates a new DateMenu
15849  * @param {Object} config Configuration options
15850  */
15851 Roo.menu.DateMenu = function(config){
15852     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15853     this.plain = true;
15854     var di = new Roo.menu.DateItem(config);
15855     this.add(di);
15856     /**
15857      * The {@link Roo.DatePicker} instance for this DateMenu
15858      * @type DatePicker
15859      */
15860     this.picker = di.picker;
15861     /**
15862      * @event select
15863      * @param {DatePicker} picker
15864      * @param {Date} date
15865      */
15866     this.relayEvents(di, ["select"]);
15867     this.on('beforeshow', function(){
15868         if(this.picker){
15869             this.picker.hideMonthPicker(false);
15870         }
15871     }, this);
15872 };
15873 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15874     cls:'x-date-menu'
15875 });/*
15876  * Based on:
15877  * Ext JS Library 1.1.1
15878  * Copyright(c) 2006-2007, Ext JS, LLC.
15879  *
15880  * Originally Released Under LGPL - original licence link has changed is not relivant.
15881  *
15882  * Fork - LGPL
15883  * <script type="text/javascript">
15884  */
15885  
15886
15887 /**
15888  * @class Roo.menu.ColorMenu
15889  * @extends Roo.menu.Menu
15890  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15891  * @constructor
15892  * Creates a new ColorMenu
15893  * @param {Object} config Configuration options
15894  */
15895 Roo.menu.ColorMenu = function(config){
15896     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15897     this.plain = true;
15898     var ci = new Roo.menu.ColorItem(config);
15899     this.add(ci);
15900     /**
15901      * The {@link Roo.ColorPalette} instance for this ColorMenu
15902      * @type ColorPalette
15903      */
15904     this.palette = ci.palette;
15905     /**
15906      * @event select
15907      * @param {ColorPalette} palette
15908      * @param {String} color
15909      */
15910     this.relayEvents(ci, ["select"]);
15911 };
15912 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15913  * Based on:
15914  * Ext JS Library 1.1.1
15915  * Copyright(c) 2006-2007, Ext JS, LLC.
15916  *
15917  * Originally Released Under LGPL - original licence link has changed is not relivant.
15918  *
15919  * Fork - LGPL
15920  * <script type="text/javascript">
15921  */
15922  
15923 /**
15924  * @class Roo.form.TextItem
15925  * @extends Roo.BoxComponent
15926  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15927  * @constructor
15928  * Creates a new TextItem
15929  * @param {Object} config Configuration options
15930  */
15931 Roo.form.TextItem = function(config){
15932     Roo.form.TextItem.superclass.constructor.call(this, config);
15933 };
15934
15935 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15936     
15937     /**
15938      * @cfg {String} tag the tag for this item (default div)
15939      */
15940     tag : 'div',
15941     /**
15942      * @cfg {String} html the content for this item
15943      */
15944     html : '',
15945     
15946     getAutoCreate : function()
15947     {
15948         var cfg = {
15949             id: this.id,
15950             tag: this.tag,
15951             html: this.html,
15952             cls: 'x-form-item'
15953         };
15954         
15955         return cfg;
15956         
15957     },
15958     
15959     onRender : function(ct, position)
15960     {
15961         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15962         
15963         if(!this.el){
15964             var cfg = this.getAutoCreate();
15965             if(!cfg.name){
15966                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15967             }
15968             if (!cfg.name.length) {
15969                 delete cfg.name;
15970             }
15971             this.el = ct.createChild(cfg, position);
15972         }
15973     },
15974     /*
15975      * setHTML
15976      * @param {String} html update the Contents of the element.
15977      */
15978     setHTML : function(html)
15979     {
15980         this.fieldEl.dom.innerHTML = html;
15981     }
15982     
15983 });/*
15984  * Based on:
15985  * Ext JS Library 1.1.1
15986  * Copyright(c) 2006-2007, Ext JS, LLC.
15987  *
15988  * Originally Released Under LGPL - original licence link has changed is not relivant.
15989  *
15990  * Fork - LGPL
15991  * <script type="text/javascript">
15992  */
15993  
15994 /**
15995  * @class Roo.form.Field
15996  * @extends Roo.BoxComponent
15997  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15998  * @constructor
15999  * Creates a new Field
16000  * @param {Object} config Configuration options
16001  */
16002 Roo.form.Field = function(config){
16003     Roo.form.Field.superclass.constructor.call(this, config);
16004 };
16005
16006 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16007     /**
16008      * @cfg {String} fieldLabel Label to use when rendering a form.
16009      */
16010        /**
16011      * @cfg {String} qtip Mouse over tip
16012      */
16013      
16014     /**
16015      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16016      */
16017     invalidClass : "x-form-invalid",
16018     /**
16019      * @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")
16020      */
16021     invalidText : "The value in this field is invalid",
16022     /**
16023      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16024      */
16025     focusClass : "x-form-focus",
16026     /**
16027      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16028       automatic validation (defaults to "keyup").
16029      */
16030     validationEvent : "keyup",
16031     /**
16032      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16033      */
16034     validateOnBlur : true,
16035     /**
16036      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16037      */
16038     validationDelay : 250,
16039     /**
16040      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16041      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16042      */
16043     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16044     /**
16045      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16046      */
16047     fieldClass : "x-form-field",
16048     /**
16049      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16050      *<pre>
16051 Value         Description
16052 -----------   ----------------------------------------------------------------------
16053 qtip          Display a quick tip when the user hovers over the field
16054 title         Display a default browser title attribute popup
16055 under         Add a block div beneath the field containing the error text
16056 side          Add an error icon to the right of the field with a popup on hover
16057 [element id]  Add the error text directly to the innerHTML of the specified element
16058 </pre>
16059      */
16060     msgTarget : 'qtip',
16061     /**
16062      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16063      */
16064     msgFx : 'normal',
16065
16066     /**
16067      * @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.
16068      */
16069     readOnly : false,
16070
16071     /**
16072      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16073      */
16074     disabled : false,
16075
16076     /**
16077      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16078      */
16079     inputType : undefined,
16080     
16081     /**
16082      * @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).
16083          */
16084         tabIndex : undefined,
16085         
16086     // private
16087     isFormField : true,
16088
16089     // private
16090     hasFocus : false,
16091     /**
16092      * @property {Roo.Element} fieldEl
16093      * Element Containing the rendered Field (with label etc.)
16094      */
16095     /**
16096      * @cfg {Mixed} value A value to initialize this field with.
16097      */
16098     value : undefined,
16099
16100     /**
16101      * @cfg {String} name The field's HTML name attribute.
16102      */
16103     /**
16104      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16105      */
16106     // private
16107     loadedValue : false,
16108      
16109      
16110         // private ??
16111         initComponent : function(){
16112         Roo.form.Field.superclass.initComponent.call(this);
16113         this.addEvents({
16114             /**
16115              * @event focus
16116              * Fires when this field receives input focus.
16117              * @param {Roo.form.Field} this
16118              */
16119             focus : true,
16120             /**
16121              * @event blur
16122              * Fires when this field loses input focus.
16123              * @param {Roo.form.Field} this
16124              */
16125             blur : true,
16126             /**
16127              * @event specialkey
16128              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16129              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16130              * @param {Roo.form.Field} this
16131              * @param {Roo.EventObject} e The event object
16132              */
16133             specialkey : true,
16134             /**
16135              * @event change
16136              * Fires just before the field blurs if the field value has changed.
16137              * @param {Roo.form.Field} this
16138              * @param {Mixed} newValue The new value
16139              * @param {Mixed} oldValue The original value
16140              */
16141             change : true,
16142             /**
16143              * @event invalid
16144              * Fires after the field has been marked as invalid.
16145              * @param {Roo.form.Field} this
16146              * @param {String} msg The validation message
16147              */
16148             invalid : true,
16149             /**
16150              * @event valid
16151              * Fires after the field has been validated with no errors.
16152              * @param {Roo.form.Field} this
16153              */
16154             valid : true,
16155              /**
16156              * @event keyup
16157              * Fires after the key up
16158              * @param {Roo.form.Field} this
16159              * @param {Roo.EventObject}  e The event Object
16160              */
16161             keyup : true
16162         });
16163     },
16164
16165     /**
16166      * Returns the name attribute of the field if available
16167      * @return {String} name The field name
16168      */
16169     getName: function(){
16170          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16171     },
16172
16173     // private
16174     onRender : function(ct, position){
16175         Roo.form.Field.superclass.onRender.call(this, ct, position);
16176         if(!this.el){
16177             var cfg = this.getAutoCreate();
16178             if(!cfg.name){
16179                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16180             }
16181             if (!cfg.name.length) {
16182                 delete cfg.name;
16183             }
16184             if(this.inputType){
16185                 cfg.type = this.inputType;
16186             }
16187             this.el = ct.createChild(cfg, position);
16188         }
16189         var type = this.el.dom.type;
16190         if(type){
16191             if(type == 'password'){
16192                 type = 'text';
16193             }
16194             this.el.addClass('x-form-'+type);
16195         }
16196         if(this.readOnly){
16197             this.el.dom.readOnly = true;
16198         }
16199         if(this.tabIndex !== undefined){
16200             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16201         }
16202
16203         this.el.addClass([this.fieldClass, this.cls]);
16204         this.initValue();
16205     },
16206
16207     /**
16208      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16209      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16210      * @return {Roo.form.Field} this
16211      */
16212     applyTo : function(target){
16213         this.allowDomMove = false;
16214         this.el = Roo.get(target);
16215         this.render(this.el.dom.parentNode);
16216         return this;
16217     },
16218
16219     // private
16220     initValue : function(){
16221         if(this.value !== undefined){
16222             this.setValue(this.value);
16223         }else if(this.el.dom.value.length > 0){
16224             this.setValue(this.el.dom.value);
16225         }
16226     },
16227
16228     /**
16229      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16230      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16231      */
16232     isDirty : function() {
16233         if(this.disabled) {
16234             return false;
16235         }
16236         return String(this.getValue()) !== String(this.originalValue);
16237     },
16238
16239     /**
16240      * stores the current value in loadedValue
16241      */
16242     resetHasChanged : function()
16243     {
16244         this.loadedValue = String(this.getValue());
16245     },
16246     /**
16247      * checks the current value against the 'loaded' value.
16248      * Note - will return false if 'resetHasChanged' has not been called first.
16249      */
16250     hasChanged : function()
16251     {
16252         if(this.disabled || this.readOnly) {
16253             return false;
16254         }
16255         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16256     },
16257     
16258     
16259     
16260     // private
16261     afterRender : function(){
16262         Roo.form.Field.superclass.afterRender.call(this);
16263         this.initEvents();
16264     },
16265
16266     // private
16267     fireKey : function(e){
16268         //Roo.log('field ' + e.getKey());
16269         if(e.isNavKeyPress()){
16270             this.fireEvent("specialkey", this, e);
16271         }
16272     },
16273
16274     /**
16275      * Resets the current field value to the originally loaded value and clears any validation messages
16276      */
16277     reset : function(){
16278         this.setValue(this.resetValue);
16279         this.originalValue = this.getValue();
16280         this.clearInvalid();
16281     },
16282
16283     // private
16284     initEvents : function(){
16285         // safari killled keypress - so keydown is now used..
16286         this.el.on("keydown" , this.fireKey,  this);
16287         this.el.on("focus", this.onFocus,  this);
16288         this.el.on("blur", this.onBlur,  this);
16289         this.el.relayEvent('keyup', this);
16290
16291         // reference to original value for reset
16292         this.originalValue = this.getValue();
16293         this.resetValue =  this.getValue();
16294     },
16295
16296     // private
16297     onFocus : function(){
16298         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16299             this.el.addClass(this.focusClass);
16300         }
16301         if(!this.hasFocus){
16302             this.hasFocus = true;
16303             this.startValue = this.getValue();
16304             this.fireEvent("focus", this);
16305         }
16306     },
16307
16308     beforeBlur : Roo.emptyFn,
16309
16310     // private
16311     onBlur : function(){
16312         this.beforeBlur();
16313         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16314             this.el.removeClass(this.focusClass);
16315         }
16316         this.hasFocus = false;
16317         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16318             this.validate();
16319         }
16320         var v = this.getValue();
16321         if(String(v) !== String(this.startValue)){
16322             this.fireEvent('change', this, v, this.startValue);
16323         }
16324         this.fireEvent("blur", this);
16325     },
16326
16327     /**
16328      * Returns whether or not the field value is currently valid
16329      * @param {Boolean} preventMark True to disable marking the field invalid
16330      * @return {Boolean} True if the value is valid, else false
16331      */
16332     isValid : function(preventMark){
16333         if(this.disabled){
16334             return true;
16335         }
16336         var restore = this.preventMark;
16337         this.preventMark = preventMark === true;
16338         var v = this.validateValue(this.processValue(this.getRawValue()));
16339         this.preventMark = restore;
16340         return v;
16341     },
16342
16343     /**
16344      * Validates the field value
16345      * @return {Boolean} True if the value is valid, else false
16346      */
16347     validate : function(){
16348         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16349             this.clearInvalid();
16350             return true;
16351         }
16352         return false;
16353     },
16354
16355     processValue : function(value){
16356         return value;
16357     },
16358
16359     // private
16360     // Subclasses should provide the validation implementation by overriding this
16361     validateValue : function(value){
16362         return true;
16363     },
16364
16365     /**
16366      * Mark this field as invalid
16367      * @param {String} msg The validation message
16368      */
16369     markInvalid : function(msg){
16370         if(!this.rendered || this.preventMark){ // not rendered
16371             return;
16372         }
16373         
16374         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16375         
16376         obj.el.addClass(this.invalidClass);
16377         msg = msg || this.invalidText;
16378         switch(this.msgTarget){
16379             case 'qtip':
16380                 obj.el.dom.qtip = msg;
16381                 obj.el.dom.qclass = 'x-form-invalid-tip';
16382                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16383                     Roo.QuickTips.enable();
16384                 }
16385                 break;
16386             case 'title':
16387                 this.el.dom.title = msg;
16388                 break;
16389             case 'under':
16390                 if(!this.errorEl){
16391                     var elp = this.el.findParent('.x-form-element', 5, true);
16392                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16393                     this.errorEl.setWidth(elp.getWidth(true)-20);
16394                 }
16395                 this.errorEl.update(msg);
16396                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16397                 break;
16398             case 'side':
16399                 if(!this.errorIcon){
16400                     var elp = this.el.findParent('.x-form-element', 5, true);
16401                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16402                 }
16403                 this.alignErrorIcon();
16404                 this.errorIcon.dom.qtip = msg;
16405                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16406                 this.errorIcon.show();
16407                 this.on('resize', this.alignErrorIcon, this);
16408                 break;
16409             default:
16410                 var t = Roo.getDom(this.msgTarget);
16411                 t.innerHTML = msg;
16412                 t.style.display = this.msgDisplay;
16413                 break;
16414         }
16415         this.fireEvent('invalid', this, msg);
16416     },
16417
16418     // private
16419     alignErrorIcon : function(){
16420         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16421     },
16422
16423     /**
16424      * Clear any invalid styles/messages for this field
16425      */
16426     clearInvalid : function(){
16427         if(!this.rendered || this.preventMark){ // not rendered
16428             return;
16429         }
16430         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16431         
16432         obj.el.removeClass(this.invalidClass);
16433         switch(this.msgTarget){
16434             case 'qtip':
16435                 obj.el.dom.qtip = '';
16436                 break;
16437             case 'title':
16438                 this.el.dom.title = '';
16439                 break;
16440             case 'under':
16441                 if(this.errorEl){
16442                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16443                 }
16444                 break;
16445             case 'side':
16446                 if(this.errorIcon){
16447                     this.errorIcon.dom.qtip = '';
16448                     this.errorIcon.hide();
16449                     this.un('resize', this.alignErrorIcon, this);
16450                 }
16451                 break;
16452             default:
16453                 var t = Roo.getDom(this.msgTarget);
16454                 t.innerHTML = '';
16455                 t.style.display = 'none';
16456                 break;
16457         }
16458         this.fireEvent('valid', this);
16459     },
16460
16461     /**
16462      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16463      * @return {Mixed} value The field value
16464      */
16465     getRawValue : function(){
16466         var v = this.el.getValue();
16467         
16468         return v;
16469     },
16470
16471     /**
16472      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16473      * @return {Mixed} value The field value
16474      */
16475     getValue : function(){
16476         var v = this.el.getValue();
16477          
16478         return v;
16479     },
16480
16481     /**
16482      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16483      * @param {Mixed} value The value to set
16484      */
16485     setRawValue : function(v){
16486         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16487     },
16488
16489     /**
16490      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16491      * @param {Mixed} value The value to set
16492      */
16493     setValue : function(v){
16494         this.value = v;
16495         if(this.rendered){
16496             this.el.dom.value = (v === null || v === undefined ? '' : v);
16497              this.validate();
16498         }
16499     },
16500
16501     adjustSize : function(w, h){
16502         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16503         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16504         return s;
16505     },
16506
16507     adjustWidth : function(tag, w){
16508         tag = tag.toLowerCase();
16509         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16510             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16511                 if(tag == 'input'){
16512                     return w + 2;
16513                 }
16514                 if(tag == 'textarea'){
16515                     return w-2;
16516                 }
16517             }else if(Roo.isOpera){
16518                 if(tag == 'input'){
16519                     return w + 2;
16520                 }
16521                 if(tag == 'textarea'){
16522                     return w-2;
16523                 }
16524             }
16525         }
16526         return w;
16527     }
16528 });
16529
16530
16531 // anything other than normal should be considered experimental
16532 Roo.form.Field.msgFx = {
16533     normal : {
16534         show: function(msgEl, f){
16535             msgEl.setDisplayed('block');
16536         },
16537
16538         hide : function(msgEl, f){
16539             msgEl.setDisplayed(false).update('');
16540         }
16541     },
16542
16543     slide : {
16544         show: function(msgEl, f){
16545             msgEl.slideIn('t', {stopFx:true});
16546         },
16547
16548         hide : function(msgEl, f){
16549             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16550         }
16551     },
16552
16553     slideRight : {
16554         show: function(msgEl, f){
16555             msgEl.fixDisplay();
16556             msgEl.alignTo(f.el, 'tl-tr');
16557             msgEl.slideIn('l', {stopFx:true});
16558         },
16559
16560         hide : function(msgEl, f){
16561             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16562         }
16563     }
16564 };/*
16565  * Based on:
16566  * Ext JS Library 1.1.1
16567  * Copyright(c) 2006-2007, Ext JS, LLC.
16568  *
16569  * Originally Released Under LGPL - original licence link has changed is not relivant.
16570  *
16571  * Fork - LGPL
16572  * <script type="text/javascript">
16573  */
16574  
16575
16576 /**
16577  * @class Roo.form.TextField
16578  * @extends Roo.form.Field
16579  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16580  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16581  * @constructor
16582  * Creates a new TextField
16583  * @param {Object} config Configuration options
16584  */
16585 Roo.form.TextField = function(config){
16586     Roo.form.TextField.superclass.constructor.call(this, config);
16587     this.addEvents({
16588         /**
16589          * @event autosize
16590          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16591          * according to the default logic, but this event provides a hook for the developer to apply additional
16592          * logic at runtime to resize the field if needed.
16593              * @param {Roo.form.Field} this This text field
16594              * @param {Number} width The new field width
16595              */
16596         autosize : true
16597     });
16598 };
16599
16600 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16601     /**
16602      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16603      */
16604     grow : false,
16605     /**
16606      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16607      */
16608     growMin : 30,
16609     /**
16610      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16611      */
16612     growMax : 800,
16613     /**
16614      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16615      */
16616     vtype : null,
16617     /**
16618      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16619      */
16620     maskRe : null,
16621     /**
16622      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16623      */
16624     disableKeyFilter : false,
16625     /**
16626      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16627      */
16628     allowBlank : true,
16629     /**
16630      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16631      */
16632     minLength : 0,
16633     /**
16634      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16635      */
16636     maxLength : Number.MAX_VALUE,
16637     /**
16638      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16639      */
16640     minLengthText : "The minimum length for this field is {0}",
16641     /**
16642      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16643      */
16644     maxLengthText : "The maximum length for this field is {0}",
16645     /**
16646      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16647      */
16648     selectOnFocus : false,
16649     /**
16650      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16651      */    
16652     allowLeadingSpace : false,
16653     /**
16654      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16655      */
16656     blankText : "This field is required",
16657     /**
16658      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16659      * If available, this function will be called only after the basic validators all return true, and will be passed the
16660      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16661      */
16662     validator : null,
16663     /**
16664      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16665      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16666      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16667      */
16668     regex : null,
16669     /**
16670      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16671      */
16672     regexText : "",
16673     /**
16674      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16675      */
16676     emptyText : null,
16677    
16678
16679     // private
16680     initEvents : function()
16681     {
16682         if (this.emptyText) {
16683             this.el.attr('placeholder', this.emptyText);
16684         }
16685         
16686         Roo.form.TextField.superclass.initEvents.call(this);
16687         if(this.validationEvent == 'keyup'){
16688             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16689             this.el.on('keyup', this.filterValidation, this);
16690         }
16691         else if(this.validationEvent !== false){
16692             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16693         }
16694         
16695         if(this.selectOnFocus){
16696             this.on("focus", this.preFocus, this);
16697         }
16698         if (!this.allowLeadingSpace) {
16699             this.on('blur', this.cleanLeadingSpace, this);
16700         }
16701         
16702         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16703             this.el.on("keypress", this.filterKeys, this);
16704         }
16705         if(this.grow){
16706             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16707             this.el.on("click", this.autoSize,  this);
16708         }
16709         if(this.el.is('input[type=password]') && Roo.isSafari){
16710             this.el.on('keydown', this.SafariOnKeyDown, this);
16711         }
16712     },
16713
16714     processValue : function(value){
16715         if(this.stripCharsRe){
16716             var newValue = value.replace(this.stripCharsRe, '');
16717             if(newValue !== value){
16718                 this.setRawValue(newValue);
16719                 return newValue;
16720             }
16721         }
16722         return value;
16723     },
16724
16725     filterValidation : function(e){
16726         if(!e.isNavKeyPress()){
16727             this.validationTask.delay(this.validationDelay);
16728         }
16729     },
16730
16731     // private
16732     onKeyUp : function(e){
16733         if(!e.isNavKeyPress()){
16734             this.autoSize();
16735         }
16736     },
16737     // private - clean the leading white space
16738     cleanLeadingSpace : function(e)
16739     {
16740         if ( this.inputType == 'file') {
16741             return;
16742         }
16743         
16744         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16745     },
16746     /**
16747      * Resets the current field value to the originally-loaded value and clears any validation messages.
16748      *  
16749      */
16750     reset : function(){
16751         Roo.form.TextField.superclass.reset.call(this);
16752        
16753     }, 
16754     // private
16755     preFocus : function(){
16756         
16757         if(this.selectOnFocus){
16758             this.el.dom.select();
16759         }
16760     },
16761
16762     
16763     // private
16764     filterKeys : function(e){
16765         var k = e.getKey();
16766         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16767             return;
16768         }
16769         var c = e.getCharCode(), cc = String.fromCharCode(c);
16770         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16771             return;
16772         }
16773         if(!this.maskRe.test(cc)){
16774             e.stopEvent();
16775         }
16776     },
16777
16778     setValue : function(v){
16779         
16780         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16781         
16782         this.autoSize();
16783     },
16784
16785     /**
16786      * Validates a value according to the field's validation rules and marks the field as invalid
16787      * if the validation fails
16788      * @param {Mixed} value The value to validate
16789      * @return {Boolean} True if the value is valid, else false
16790      */
16791     validateValue : function(value){
16792         if(value.length < 1)  { // if it's blank
16793              if(this.allowBlank){
16794                 this.clearInvalid();
16795                 return true;
16796              }else{
16797                 this.markInvalid(this.blankText);
16798                 return false;
16799              }
16800         }
16801         if(value.length < this.minLength){
16802             this.markInvalid(String.format(this.minLengthText, this.minLength));
16803             return false;
16804         }
16805         if(value.length > this.maxLength){
16806             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16807             return false;
16808         }
16809         if(this.vtype){
16810             var vt = Roo.form.VTypes;
16811             if(!vt[this.vtype](value, this)){
16812                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16813                 return false;
16814             }
16815         }
16816         if(typeof this.validator == "function"){
16817             var msg = this.validator(value);
16818             if(msg !== true){
16819                 this.markInvalid(msg);
16820                 return false;
16821             }
16822         }
16823         if(this.regex && !this.regex.test(value)){
16824             this.markInvalid(this.regexText);
16825             return false;
16826         }
16827         return true;
16828     },
16829
16830     /**
16831      * Selects text in this field
16832      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16833      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16834      */
16835     selectText : function(start, end){
16836         var v = this.getRawValue();
16837         if(v.length > 0){
16838             start = start === undefined ? 0 : start;
16839             end = end === undefined ? v.length : end;
16840             var d = this.el.dom;
16841             if(d.setSelectionRange){
16842                 d.setSelectionRange(start, end);
16843             }else if(d.createTextRange){
16844                 var range = d.createTextRange();
16845                 range.moveStart("character", start);
16846                 range.moveEnd("character", v.length-end);
16847                 range.select();
16848             }
16849         }
16850     },
16851
16852     /**
16853      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16854      * This only takes effect if grow = true, and fires the autosize event.
16855      */
16856     autoSize : function(){
16857         if(!this.grow || !this.rendered){
16858             return;
16859         }
16860         if(!this.metrics){
16861             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16862         }
16863         var el = this.el;
16864         var v = el.dom.value;
16865         var d = document.createElement('div');
16866         d.appendChild(document.createTextNode(v));
16867         v = d.innerHTML;
16868         d = null;
16869         v += "&#160;";
16870         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16871         this.el.setWidth(w);
16872         this.fireEvent("autosize", this, w);
16873     },
16874     
16875     // private
16876     SafariOnKeyDown : function(event)
16877     {
16878         // this is a workaround for a password hang bug on chrome/ webkit.
16879         
16880         var isSelectAll = false;
16881         
16882         if(this.el.dom.selectionEnd > 0){
16883             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16884         }
16885         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16886             event.preventDefault();
16887             this.setValue('');
16888             return;
16889         }
16890         
16891         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16892             
16893             event.preventDefault();
16894             // this is very hacky as keydown always get's upper case.
16895             
16896             var cc = String.fromCharCode(event.getCharCode());
16897             
16898             
16899             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16900             
16901         }
16902         
16903         
16904     }
16905 });/*
16906  * Based on:
16907  * Ext JS Library 1.1.1
16908  * Copyright(c) 2006-2007, Ext JS, LLC.
16909  *
16910  * Originally Released Under LGPL - original licence link has changed is not relivant.
16911  *
16912  * Fork - LGPL
16913  * <script type="text/javascript">
16914  */
16915  
16916 /**
16917  * @class Roo.form.Hidden
16918  * @extends Roo.form.TextField
16919  * Simple Hidden element used on forms 
16920  * 
16921  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16922  * 
16923  * @constructor
16924  * Creates a new Hidden form element.
16925  * @param {Object} config Configuration options
16926  */
16927
16928
16929
16930 // easy hidden field...
16931 Roo.form.Hidden = function(config){
16932     Roo.form.Hidden.superclass.constructor.call(this, config);
16933 };
16934   
16935 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16936     fieldLabel:      '',
16937     inputType:      'hidden',
16938     width:          50,
16939     allowBlank:     true,
16940     labelSeparator: '',
16941     hidden:         true,
16942     itemCls :       'x-form-item-display-none'
16943
16944
16945 });
16946
16947
16948 /*
16949  * Based on:
16950  * Ext JS Library 1.1.1
16951  * Copyright(c) 2006-2007, Ext JS, LLC.
16952  *
16953  * Originally Released Under LGPL - original licence link has changed is not relivant.
16954  *
16955  * Fork - LGPL
16956  * <script type="text/javascript">
16957  */
16958  
16959 /**
16960  * @class Roo.form.TriggerField
16961  * @extends Roo.form.TextField
16962  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16963  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16964  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16965  * for which you can provide a custom implementation.  For example:
16966  * <pre><code>
16967 var trigger = new Roo.form.TriggerField();
16968 trigger.onTriggerClick = myTriggerFn;
16969 trigger.applyTo('my-field');
16970 </code></pre>
16971  *
16972  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16973  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16974  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16975  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16976  * @constructor
16977  * Create a new TriggerField.
16978  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16979  * to the base TextField)
16980  */
16981 Roo.form.TriggerField = function(config){
16982     this.mimicing = false;
16983     Roo.form.TriggerField.superclass.constructor.call(this, config);
16984 };
16985
16986 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16987     /**
16988      * @cfg {String} triggerClass A CSS class to apply to the trigger
16989      */
16990     /**
16991      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16992      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16993      */
16994     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16995     /**
16996      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16997      */
16998     hideTrigger:false,
16999
17000     /** @cfg {Boolean} grow @hide */
17001     /** @cfg {Number} growMin @hide */
17002     /** @cfg {Number} growMax @hide */
17003
17004     /**
17005      * @hide 
17006      * @method
17007      */
17008     autoSize: Roo.emptyFn,
17009     // private
17010     monitorTab : true,
17011     // private
17012     deferHeight : true,
17013
17014     
17015     actionMode : 'wrap',
17016     // private
17017     onResize : function(w, h){
17018         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17019         if(typeof w == 'number'){
17020             var x = w - this.trigger.getWidth();
17021             this.el.setWidth(this.adjustWidth('input', x));
17022             this.trigger.setStyle('left', x+'px');
17023         }
17024     },
17025
17026     // private
17027     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17028
17029     // private
17030     getResizeEl : function(){
17031         return this.wrap;
17032     },
17033
17034     // private
17035     getPositionEl : function(){
17036         return this.wrap;
17037     },
17038
17039     // private
17040     alignErrorIcon : function(){
17041         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17042     },
17043
17044     // private
17045     onRender : function(ct, position){
17046         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17047         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17048         this.trigger = this.wrap.createChild(this.triggerConfig ||
17049                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17050         if(this.hideTrigger){
17051             this.trigger.setDisplayed(false);
17052         }
17053         this.initTrigger();
17054         if(!this.width){
17055             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17056         }
17057     },
17058
17059     // private
17060     initTrigger : function(){
17061         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17062         this.trigger.addClassOnOver('x-form-trigger-over');
17063         this.trigger.addClassOnClick('x-form-trigger-click');
17064     },
17065
17066     // private
17067     onDestroy : function(){
17068         if(this.trigger){
17069             this.trigger.removeAllListeners();
17070             this.trigger.remove();
17071         }
17072         if(this.wrap){
17073             this.wrap.remove();
17074         }
17075         Roo.form.TriggerField.superclass.onDestroy.call(this);
17076     },
17077
17078     // private
17079     onFocus : function(){
17080         Roo.form.TriggerField.superclass.onFocus.call(this);
17081         if(!this.mimicing){
17082             this.wrap.addClass('x-trigger-wrap-focus');
17083             this.mimicing = true;
17084             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17085             if(this.monitorTab){
17086                 this.el.on("keydown", this.checkTab, this);
17087             }
17088         }
17089     },
17090
17091     // private
17092     checkTab : function(e){
17093         if(e.getKey() == e.TAB){
17094             this.triggerBlur();
17095         }
17096     },
17097
17098     // private
17099     onBlur : function(){
17100         // do nothing
17101     },
17102
17103     // private
17104     mimicBlur : function(e, t){
17105         if(!this.wrap.contains(t) && this.validateBlur()){
17106             this.triggerBlur();
17107         }
17108     },
17109
17110     // private
17111     triggerBlur : function(){
17112         this.mimicing = false;
17113         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17114         if(this.monitorTab){
17115             this.el.un("keydown", this.checkTab, this);
17116         }
17117         this.wrap.removeClass('x-trigger-wrap-focus');
17118         Roo.form.TriggerField.superclass.onBlur.call(this);
17119     },
17120
17121     // private
17122     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17123     validateBlur : function(e, t){
17124         return true;
17125     },
17126
17127     // private
17128     onDisable : function(){
17129         Roo.form.TriggerField.superclass.onDisable.call(this);
17130         if(this.wrap){
17131             this.wrap.addClass('x-item-disabled');
17132         }
17133     },
17134
17135     // private
17136     onEnable : function(){
17137         Roo.form.TriggerField.superclass.onEnable.call(this);
17138         if(this.wrap){
17139             this.wrap.removeClass('x-item-disabled');
17140         }
17141     },
17142
17143     // private
17144     onShow : function(){
17145         var ae = this.getActionEl();
17146         
17147         if(ae){
17148             ae.dom.style.display = '';
17149             ae.dom.style.visibility = 'visible';
17150         }
17151     },
17152
17153     // private
17154     
17155     onHide : function(){
17156         var ae = this.getActionEl();
17157         ae.dom.style.display = 'none';
17158     },
17159
17160     /**
17161      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17162      * by an implementing function.
17163      * @method
17164      * @param {EventObject} e
17165      */
17166     onTriggerClick : Roo.emptyFn
17167 });
17168
17169 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17170 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17171 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17172 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17173     initComponent : function(){
17174         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17175
17176         this.triggerConfig = {
17177             tag:'span', cls:'x-form-twin-triggers', cn:[
17178             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17179             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17180         ]};
17181     },
17182
17183     getTrigger : function(index){
17184         return this.triggers[index];
17185     },
17186
17187     initTrigger : function(){
17188         var ts = this.trigger.select('.x-form-trigger', true);
17189         this.wrap.setStyle('overflow', 'hidden');
17190         var triggerField = this;
17191         ts.each(function(t, all, index){
17192             t.hide = function(){
17193                 var w = triggerField.wrap.getWidth();
17194                 this.dom.style.display = 'none';
17195                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17196             };
17197             t.show = function(){
17198                 var w = triggerField.wrap.getWidth();
17199                 this.dom.style.display = '';
17200                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17201             };
17202             var triggerIndex = 'Trigger'+(index+1);
17203
17204             if(this['hide'+triggerIndex]){
17205                 t.dom.style.display = 'none';
17206             }
17207             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17208             t.addClassOnOver('x-form-trigger-over');
17209             t.addClassOnClick('x-form-trigger-click');
17210         }, this);
17211         this.triggers = ts.elements;
17212     },
17213
17214     onTrigger1Click : Roo.emptyFn,
17215     onTrigger2Click : Roo.emptyFn
17216 });/*
17217  * Based on:
17218  * Ext JS Library 1.1.1
17219  * Copyright(c) 2006-2007, Ext JS, LLC.
17220  *
17221  * Originally Released Under LGPL - original licence link has changed is not relivant.
17222  *
17223  * Fork - LGPL
17224  * <script type="text/javascript">
17225  */
17226  
17227 /**
17228  * @class Roo.form.TextArea
17229  * @extends Roo.form.TextField
17230  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17231  * support for auto-sizing.
17232  * @constructor
17233  * Creates a new TextArea
17234  * @param {Object} config Configuration options
17235  */
17236 Roo.form.TextArea = function(config){
17237     Roo.form.TextArea.superclass.constructor.call(this, config);
17238     // these are provided exchanges for backwards compat
17239     // minHeight/maxHeight were replaced by growMin/growMax to be
17240     // compatible with TextField growing config values
17241     if(this.minHeight !== undefined){
17242         this.growMin = this.minHeight;
17243     }
17244     if(this.maxHeight !== undefined){
17245         this.growMax = this.maxHeight;
17246     }
17247 };
17248
17249 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17250     /**
17251      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17252      */
17253     growMin : 60,
17254     /**
17255      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17256      */
17257     growMax: 1000,
17258     /**
17259      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17260      * in the field (equivalent to setting overflow: hidden, defaults to false)
17261      */
17262     preventScrollbars: false,
17263     /**
17264      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17265      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17266      */
17267
17268     // private
17269     onRender : function(ct, position){
17270         if(!this.el){
17271             this.defaultAutoCreate = {
17272                 tag: "textarea",
17273                 style:"width:300px;height:60px;",
17274                 autocomplete: "new-password"
17275             };
17276         }
17277         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17278         if(this.grow){
17279             this.textSizeEl = Roo.DomHelper.append(document.body, {
17280                 tag: "pre", cls: "x-form-grow-sizer"
17281             });
17282             if(this.preventScrollbars){
17283                 this.el.setStyle("overflow", "hidden");
17284             }
17285             this.el.setHeight(this.growMin);
17286         }
17287     },
17288
17289     onDestroy : function(){
17290         if(this.textSizeEl){
17291             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17292         }
17293         Roo.form.TextArea.superclass.onDestroy.call(this);
17294     },
17295
17296     // private
17297     onKeyUp : function(e){
17298         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17299             this.autoSize();
17300         }
17301     },
17302
17303     /**
17304      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17305      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17306      */
17307     autoSize : function(){
17308         if(!this.grow || !this.textSizeEl){
17309             return;
17310         }
17311         var el = this.el;
17312         var v = el.dom.value;
17313         var ts = this.textSizeEl;
17314
17315         ts.innerHTML = '';
17316         ts.appendChild(document.createTextNode(v));
17317         v = ts.innerHTML;
17318
17319         Roo.fly(ts).setWidth(this.el.getWidth());
17320         if(v.length < 1){
17321             v = "&#160;&#160;";
17322         }else{
17323             if(Roo.isIE){
17324                 v = v.replace(/\n/g, '<p>&#160;</p>');
17325             }
17326             v += "&#160;\n&#160;";
17327         }
17328         ts.innerHTML = v;
17329         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17330         if(h != this.lastHeight){
17331             this.lastHeight = h;
17332             this.el.setHeight(h);
17333             this.fireEvent("autosize", this, h);
17334         }
17335     }
17336 });/*
17337  * Based on:
17338  * Ext JS Library 1.1.1
17339  * Copyright(c) 2006-2007, Ext JS, LLC.
17340  *
17341  * Originally Released Under LGPL - original licence link has changed is not relivant.
17342  *
17343  * Fork - LGPL
17344  * <script type="text/javascript">
17345  */
17346  
17347
17348 /**
17349  * @class Roo.form.NumberField
17350  * @extends Roo.form.TextField
17351  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17352  * @constructor
17353  * Creates a new NumberField
17354  * @param {Object} config Configuration options
17355  */
17356 Roo.form.NumberField = function(config){
17357     Roo.form.NumberField.superclass.constructor.call(this, config);
17358 };
17359
17360 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17361     /**
17362      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17363      */
17364     fieldClass: "x-form-field x-form-num-field",
17365     /**
17366      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17367      */
17368     allowDecimals : true,
17369     /**
17370      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17371      */
17372     decimalSeparator : ".",
17373     /**
17374      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17375      */
17376     decimalPrecision : 2,
17377     /**
17378      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17379      */
17380     allowNegative : true,
17381     /**
17382      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17383      */
17384     minValue : Number.NEGATIVE_INFINITY,
17385     /**
17386      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17387      */
17388     maxValue : Number.MAX_VALUE,
17389     /**
17390      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17391      */
17392     minText : "The minimum value for this field is {0}",
17393     /**
17394      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17395      */
17396     maxText : "The maximum value for this field is {0}",
17397     /**
17398      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17399      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17400      */
17401     nanText : "{0} is not a valid number",
17402
17403     // private
17404     initEvents : function(){
17405         Roo.form.NumberField.superclass.initEvents.call(this);
17406         var allowed = "0123456789";
17407         if(this.allowDecimals){
17408             allowed += this.decimalSeparator;
17409         }
17410         if(this.allowNegative){
17411             allowed += "-";
17412         }
17413         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17414         var keyPress = function(e){
17415             var k = e.getKey();
17416             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17417                 return;
17418             }
17419             var c = e.getCharCode();
17420             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17421                 e.stopEvent();
17422             }
17423         };
17424         this.el.on("keypress", keyPress, this);
17425     },
17426
17427     // private
17428     validateValue : function(value){
17429         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17430             return false;
17431         }
17432         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17433              return true;
17434         }
17435         var num = this.parseValue(value);
17436         if(isNaN(num)){
17437             this.markInvalid(String.format(this.nanText, value));
17438             return false;
17439         }
17440         if(num < this.minValue){
17441             this.markInvalid(String.format(this.minText, this.minValue));
17442             return false;
17443         }
17444         if(num > this.maxValue){
17445             this.markInvalid(String.format(this.maxText, this.maxValue));
17446             return false;
17447         }
17448         return true;
17449     },
17450
17451     getValue : function(){
17452         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17453     },
17454
17455     // private
17456     parseValue : function(value){
17457         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17458         return isNaN(value) ? '' : value;
17459     },
17460
17461     // private
17462     fixPrecision : function(value){
17463         var nan = isNaN(value);
17464         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17465             return nan ? '' : value;
17466         }
17467         return parseFloat(value).toFixed(this.decimalPrecision);
17468     },
17469
17470     setValue : function(v){
17471         v = this.fixPrecision(v);
17472         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17473     },
17474
17475     // private
17476     decimalPrecisionFcn : function(v){
17477         return Math.floor(v);
17478     },
17479
17480     beforeBlur : function(){
17481         var v = this.parseValue(this.getRawValue());
17482         if(v){
17483             this.setValue(v);
17484         }
17485     }
17486 });/*
17487  * Based on:
17488  * Ext JS Library 1.1.1
17489  * Copyright(c) 2006-2007, Ext JS, LLC.
17490  *
17491  * Originally Released Under LGPL - original licence link has changed is not relivant.
17492  *
17493  * Fork - LGPL
17494  * <script type="text/javascript">
17495  */
17496  
17497 /**
17498  * @class Roo.form.DateField
17499  * @extends Roo.form.TriggerField
17500  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17501 * @constructor
17502 * Create a new DateField
17503 * @param {Object} config
17504  */
17505 Roo.form.DateField = function(config)
17506 {
17507     Roo.form.DateField.superclass.constructor.call(this, config);
17508     
17509       this.addEvents({
17510          
17511         /**
17512          * @event select
17513          * Fires when a date is selected
17514              * @param {Roo.form.DateField} combo This combo box
17515              * @param {Date} date The date selected
17516              */
17517         'select' : true
17518          
17519     });
17520     
17521     
17522     if(typeof this.minValue == "string") {
17523         this.minValue = this.parseDate(this.minValue);
17524     }
17525     if(typeof this.maxValue == "string") {
17526         this.maxValue = this.parseDate(this.maxValue);
17527     }
17528     this.ddMatch = null;
17529     if(this.disabledDates){
17530         var dd = this.disabledDates;
17531         var re = "(?:";
17532         for(var i = 0; i < dd.length; i++){
17533             re += dd[i];
17534             if(i != dd.length-1) {
17535                 re += "|";
17536             }
17537         }
17538         this.ddMatch = new RegExp(re + ")");
17539     }
17540 };
17541
17542 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17543     /**
17544      * @cfg {String} format
17545      * The default date format string which can be overriden for localization support.  The format must be
17546      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17547      */
17548     format : "m/d/y",
17549     /**
17550      * @cfg {String} altFormats
17551      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17552      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17553      */
17554     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17555     /**
17556      * @cfg {Array} disabledDays
17557      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17558      */
17559     disabledDays : null,
17560     /**
17561      * @cfg {String} disabledDaysText
17562      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17563      */
17564     disabledDaysText : "Disabled",
17565     /**
17566      * @cfg {Array} disabledDates
17567      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17568      * expression so they are very powerful. Some examples:
17569      * <ul>
17570      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17571      * <li>["03/08", "09/16"] would disable those days for every year</li>
17572      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17573      * <li>["03/../2006"] would disable every day in March 2006</li>
17574      * <li>["^03"] would disable every day in every March</li>
17575      * </ul>
17576      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17577      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17578      */
17579     disabledDates : null,
17580     /**
17581      * @cfg {String} disabledDatesText
17582      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17583      */
17584     disabledDatesText : "Disabled",
17585     /**
17586      * @cfg {Date/String} minValue
17587      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17588      * valid format (defaults to null).
17589      */
17590     minValue : null,
17591     /**
17592      * @cfg {Date/String} maxValue
17593      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17594      * valid format (defaults to null).
17595      */
17596     maxValue : null,
17597     /**
17598      * @cfg {String} minText
17599      * The error text to display when the date in the cell is before minValue (defaults to
17600      * 'The date in this field must be after {minValue}').
17601      */
17602     minText : "The date in this field must be equal to or after {0}",
17603     /**
17604      * @cfg {String} maxText
17605      * The error text to display when the date in the cell is after maxValue (defaults to
17606      * 'The date in this field must be before {maxValue}').
17607      */
17608     maxText : "The date in this field must be equal to or before {0}",
17609     /**
17610      * @cfg {String} invalidText
17611      * The error text to display when the date in the field is invalid (defaults to
17612      * '{value} is not a valid date - it must be in the format {format}').
17613      */
17614     invalidText : "{0} is not a valid date - it must be in the format {1}",
17615     /**
17616      * @cfg {String} triggerClass
17617      * An additional CSS class used to style the trigger button.  The trigger will always get the
17618      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17619      * which displays a calendar icon).
17620      */
17621     triggerClass : 'x-form-date-trigger',
17622     
17623
17624     /**
17625      * @cfg {Boolean} useIso
17626      * if enabled, then the date field will use a hidden field to store the 
17627      * real value as iso formated date. default (false)
17628      */ 
17629     useIso : false,
17630     /**
17631      * @cfg {String/Object} autoCreate
17632      * A DomHelper element spec, or true for a default element spec (defaults to
17633      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17634      */ 
17635     // private
17636     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17637     
17638     // private
17639     hiddenField: false,
17640     
17641     onRender : function(ct, position)
17642     {
17643         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17644         if (this.useIso) {
17645             //this.el.dom.removeAttribute('name'); 
17646             Roo.log("Changing name?");
17647             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17648             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17649                     'before', true);
17650             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17651             // prevent input submission
17652             this.hiddenName = this.name;
17653         }
17654             
17655             
17656     },
17657     
17658     // private
17659     validateValue : function(value)
17660     {
17661         value = this.formatDate(value);
17662         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17663             Roo.log('super failed');
17664             return false;
17665         }
17666         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17667              return true;
17668         }
17669         var svalue = value;
17670         value = this.parseDate(value);
17671         if(!value){
17672             Roo.log('parse date failed' + svalue);
17673             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17674             return false;
17675         }
17676         var time = value.getTime();
17677         if(this.minValue && time < this.minValue.getTime()){
17678             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17679             return false;
17680         }
17681         if(this.maxValue && time > this.maxValue.getTime()){
17682             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17683             return false;
17684         }
17685         if(this.disabledDays){
17686             var day = value.getDay();
17687             for(var i = 0; i < this.disabledDays.length; i++) {
17688                 if(day === this.disabledDays[i]){
17689                     this.markInvalid(this.disabledDaysText);
17690                     return false;
17691                 }
17692             }
17693         }
17694         var fvalue = this.formatDate(value);
17695         if(this.ddMatch && this.ddMatch.test(fvalue)){
17696             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17697             return false;
17698         }
17699         return true;
17700     },
17701
17702     // private
17703     // Provides logic to override the default TriggerField.validateBlur which just returns true
17704     validateBlur : function(){
17705         return !this.menu || !this.menu.isVisible();
17706     },
17707     
17708     getName: function()
17709     {
17710         // returns hidden if it's set..
17711         if (!this.rendered) {return ''};
17712         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17713         
17714     },
17715
17716     /**
17717      * Returns the current date value of the date field.
17718      * @return {Date} The date value
17719      */
17720     getValue : function(){
17721         
17722         return  this.hiddenField ?
17723                 this.hiddenField.value :
17724                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17725     },
17726
17727     /**
17728      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17729      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17730      * (the default format used is "m/d/y").
17731      * <br />Usage:
17732      * <pre><code>
17733 //All of these calls set the same date value (May 4, 2006)
17734
17735 //Pass a date object:
17736 var dt = new Date('5/4/06');
17737 dateField.setValue(dt);
17738
17739 //Pass a date string (default format):
17740 dateField.setValue('5/4/06');
17741
17742 //Pass a date string (custom format):
17743 dateField.format = 'Y-m-d';
17744 dateField.setValue('2006-5-4');
17745 </code></pre>
17746      * @param {String/Date} date The date or valid date string
17747      */
17748     setValue : function(date){
17749         if (this.hiddenField) {
17750             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17751         }
17752         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17753         // make sure the value field is always stored as a date..
17754         this.value = this.parseDate(date);
17755         
17756         
17757     },
17758
17759     // private
17760     parseDate : function(value){
17761         if(!value || value instanceof Date){
17762             return value;
17763         }
17764         var v = Date.parseDate(value, this.format);
17765          if (!v && this.useIso) {
17766             v = Date.parseDate(value, 'Y-m-d');
17767         }
17768         if(!v && this.altFormats){
17769             if(!this.altFormatsArray){
17770                 this.altFormatsArray = this.altFormats.split("|");
17771             }
17772             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17773                 v = Date.parseDate(value, this.altFormatsArray[i]);
17774             }
17775         }
17776         return v;
17777     },
17778
17779     // private
17780     formatDate : function(date, fmt){
17781         return (!date || !(date instanceof Date)) ?
17782                date : date.dateFormat(fmt || this.format);
17783     },
17784
17785     // private
17786     menuListeners : {
17787         select: function(m, d){
17788             
17789             this.setValue(d);
17790             this.fireEvent('select', this, d);
17791         },
17792         show : function(){ // retain focus styling
17793             this.onFocus();
17794         },
17795         hide : function(){
17796             this.focus.defer(10, this);
17797             var ml = this.menuListeners;
17798             this.menu.un("select", ml.select,  this);
17799             this.menu.un("show", ml.show,  this);
17800             this.menu.un("hide", ml.hide,  this);
17801         }
17802     },
17803
17804     // private
17805     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17806     onTriggerClick : function(){
17807         if(this.disabled){
17808             return;
17809         }
17810         if(this.menu == null){
17811             this.menu = new Roo.menu.DateMenu();
17812         }
17813         Roo.apply(this.menu.picker,  {
17814             showClear: this.allowBlank,
17815             minDate : this.minValue,
17816             maxDate : this.maxValue,
17817             disabledDatesRE : this.ddMatch,
17818             disabledDatesText : this.disabledDatesText,
17819             disabledDays : this.disabledDays,
17820             disabledDaysText : this.disabledDaysText,
17821             format : this.useIso ? 'Y-m-d' : this.format,
17822             minText : String.format(this.minText, this.formatDate(this.minValue)),
17823             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17824         });
17825         this.menu.on(Roo.apply({}, this.menuListeners, {
17826             scope:this
17827         }));
17828         this.menu.picker.setValue(this.getValue() || new Date());
17829         this.menu.show(this.el, "tl-bl?");
17830     },
17831
17832     beforeBlur : function(){
17833         var v = this.parseDate(this.getRawValue());
17834         if(v){
17835             this.setValue(v);
17836         }
17837     },
17838
17839     /*@
17840      * overide
17841      * 
17842      */
17843     isDirty : function() {
17844         if(this.disabled) {
17845             return false;
17846         }
17847         
17848         if(typeof(this.startValue) === 'undefined'){
17849             return false;
17850         }
17851         
17852         return String(this.getValue()) !== String(this.startValue);
17853         
17854     },
17855     // @overide
17856     cleanLeadingSpace : function(e)
17857     {
17858        return;
17859     }
17860     
17861 });/*
17862  * Based on:
17863  * Ext JS Library 1.1.1
17864  * Copyright(c) 2006-2007, Ext JS, LLC.
17865  *
17866  * Originally Released Under LGPL - original licence link has changed is not relivant.
17867  *
17868  * Fork - LGPL
17869  * <script type="text/javascript">
17870  */
17871  
17872 /**
17873  * @class Roo.form.MonthField
17874  * @extends Roo.form.TriggerField
17875  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17876 * @constructor
17877 * Create a new MonthField
17878 * @param {Object} config
17879  */
17880 Roo.form.MonthField = function(config){
17881     
17882     Roo.form.MonthField.superclass.constructor.call(this, config);
17883     
17884       this.addEvents({
17885          
17886         /**
17887          * @event select
17888          * Fires when a date is selected
17889              * @param {Roo.form.MonthFieeld} combo This combo box
17890              * @param {Date} date The date selected
17891              */
17892         'select' : true
17893          
17894     });
17895     
17896     
17897     if(typeof this.minValue == "string") {
17898         this.minValue = this.parseDate(this.minValue);
17899     }
17900     if(typeof this.maxValue == "string") {
17901         this.maxValue = this.parseDate(this.maxValue);
17902     }
17903     this.ddMatch = null;
17904     if(this.disabledDates){
17905         var dd = this.disabledDates;
17906         var re = "(?:";
17907         for(var i = 0; i < dd.length; i++){
17908             re += dd[i];
17909             if(i != dd.length-1) {
17910                 re += "|";
17911             }
17912         }
17913         this.ddMatch = new RegExp(re + ")");
17914     }
17915 };
17916
17917 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17918     /**
17919      * @cfg {String} format
17920      * The default date format string which can be overriden for localization support.  The format must be
17921      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17922      */
17923     format : "M Y",
17924     /**
17925      * @cfg {String} altFormats
17926      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17927      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17928      */
17929     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17930     /**
17931      * @cfg {Array} disabledDays
17932      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17933      */
17934     disabledDays : [0,1,2,3,4,5,6],
17935     /**
17936      * @cfg {String} disabledDaysText
17937      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17938      */
17939     disabledDaysText : "Disabled",
17940     /**
17941      * @cfg {Array} disabledDates
17942      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17943      * expression so they are very powerful. Some examples:
17944      * <ul>
17945      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17946      * <li>["03/08", "09/16"] would disable those days for every year</li>
17947      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17948      * <li>["03/../2006"] would disable every day in March 2006</li>
17949      * <li>["^03"] would disable every day in every March</li>
17950      * </ul>
17951      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17952      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17953      */
17954     disabledDates : null,
17955     /**
17956      * @cfg {String} disabledDatesText
17957      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17958      */
17959     disabledDatesText : "Disabled",
17960     /**
17961      * @cfg {Date/String} minValue
17962      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17963      * valid format (defaults to null).
17964      */
17965     minValue : null,
17966     /**
17967      * @cfg {Date/String} maxValue
17968      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17969      * valid format (defaults to null).
17970      */
17971     maxValue : null,
17972     /**
17973      * @cfg {String} minText
17974      * The error text to display when the date in the cell is before minValue (defaults to
17975      * 'The date in this field must be after {minValue}').
17976      */
17977     minText : "The date in this field must be equal to or after {0}",
17978     /**
17979      * @cfg {String} maxTextf
17980      * The error text to display when the date in the cell is after maxValue (defaults to
17981      * 'The date in this field must be before {maxValue}').
17982      */
17983     maxText : "The date in this field must be equal to or before {0}",
17984     /**
17985      * @cfg {String} invalidText
17986      * The error text to display when the date in the field is invalid (defaults to
17987      * '{value} is not a valid date - it must be in the format {format}').
17988      */
17989     invalidText : "{0} is not a valid date - it must be in the format {1}",
17990     /**
17991      * @cfg {String} triggerClass
17992      * An additional CSS class used to style the trigger button.  The trigger will always get the
17993      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17994      * which displays a calendar icon).
17995      */
17996     triggerClass : 'x-form-date-trigger',
17997     
17998
17999     /**
18000      * @cfg {Boolean} useIso
18001      * if enabled, then the date field will use a hidden field to store the 
18002      * real value as iso formated date. default (true)
18003      */ 
18004     useIso : true,
18005     /**
18006      * @cfg {String/Object} autoCreate
18007      * A DomHelper element spec, or true for a default element spec (defaults to
18008      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18009      */ 
18010     // private
18011     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18012     
18013     // private
18014     hiddenField: false,
18015     
18016     hideMonthPicker : false,
18017     
18018     onRender : function(ct, position)
18019     {
18020         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18021         if (this.useIso) {
18022             this.el.dom.removeAttribute('name'); 
18023             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18024                     'before', true);
18025             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18026             // prevent input submission
18027             this.hiddenName = this.name;
18028         }
18029             
18030             
18031     },
18032     
18033     // private
18034     validateValue : function(value)
18035     {
18036         value = this.formatDate(value);
18037         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18038             return false;
18039         }
18040         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18041              return true;
18042         }
18043         var svalue = value;
18044         value = this.parseDate(value);
18045         if(!value){
18046             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18047             return false;
18048         }
18049         var time = value.getTime();
18050         if(this.minValue && time < this.minValue.getTime()){
18051             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18052             return false;
18053         }
18054         if(this.maxValue && time > this.maxValue.getTime()){
18055             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18056             return false;
18057         }
18058         /*if(this.disabledDays){
18059             var day = value.getDay();
18060             for(var i = 0; i < this.disabledDays.length; i++) {
18061                 if(day === this.disabledDays[i]){
18062                     this.markInvalid(this.disabledDaysText);
18063                     return false;
18064                 }
18065             }
18066         }
18067         */
18068         var fvalue = this.formatDate(value);
18069         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18070             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18071             return false;
18072         }
18073         */
18074         return true;
18075     },
18076
18077     // private
18078     // Provides logic to override the default TriggerField.validateBlur which just returns true
18079     validateBlur : function(){
18080         return !this.menu || !this.menu.isVisible();
18081     },
18082
18083     /**
18084      * Returns the current date value of the date field.
18085      * @return {Date} The date value
18086      */
18087     getValue : function(){
18088         
18089         
18090         
18091         return  this.hiddenField ?
18092                 this.hiddenField.value :
18093                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18094     },
18095
18096     /**
18097      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18098      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18099      * (the default format used is "m/d/y").
18100      * <br />Usage:
18101      * <pre><code>
18102 //All of these calls set the same date value (May 4, 2006)
18103
18104 //Pass a date object:
18105 var dt = new Date('5/4/06');
18106 monthField.setValue(dt);
18107
18108 //Pass a date string (default format):
18109 monthField.setValue('5/4/06');
18110
18111 //Pass a date string (custom format):
18112 monthField.format = 'Y-m-d';
18113 monthField.setValue('2006-5-4');
18114 </code></pre>
18115      * @param {String/Date} date The date or valid date string
18116      */
18117     setValue : function(date){
18118         Roo.log('month setValue' + date);
18119         // can only be first of month..
18120         
18121         var val = this.parseDate(date);
18122         
18123         if (this.hiddenField) {
18124             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18125         }
18126         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18127         this.value = this.parseDate(date);
18128     },
18129
18130     // private
18131     parseDate : function(value){
18132         if(!value || value instanceof Date){
18133             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18134             return value;
18135         }
18136         var v = Date.parseDate(value, this.format);
18137         if (!v && this.useIso) {
18138             v = Date.parseDate(value, 'Y-m-d');
18139         }
18140         if (v) {
18141             // 
18142             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18143         }
18144         
18145         
18146         if(!v && this.altFormats){
18147             if(!this.altFormatsArray){
18148                 this.altFormatsArray = this.altFormats.split("|");
18149             }
18150             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18151                 v = Date.parseDate(value, this.altFormatsArray[i]);
18152             }
18153         }
18154         return v;
18155     },
18156
18157     // private
18158     formatDate : function(date, fmt){
18159         return (!date || !(date instanceof Date)) ?
18160                date : date.dateFormat(fmt || this.format);
18161     },
18162
18163     // private
18164     menuListeners : {
18165         select: function(m, d){
18166             this.setValue(d);
18167             this.fireEvent('select', this, d);
18168         },
18169         show : function(){ // retain focus styling
18170             this.onFocus();
18171         },
18172         hide : function(){
18173             this.focus.defer(10, this);
18174             var ml = this.menuListeners;
18175             this.menu.un("select", ml.select,  this);
18176             this.menu.un("show", ml.show,  this);
18177             this.menu.un("hide", ml.hide,  this);
18178         }
18179     },
18180     // private
18181     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18182     onTriggerClick : function(){
18183         if(this.disabled){
18184             return;
18185         }
18186         if(this.menu == null){
18187             this.menu = new Roo.menu.DateMenu();
18188            
18189         }
18190         
18191         Roo.apply(this.menu.picker,  {
18192             
18193             showClear: this.allowBlank,
18194             minDate : this.minValue,
18195             maxDate : this.maxValue,
18196             disabledDatesRE : this.ddMatch,
18197             disabledDatesText : this.disabledDatesText,
18198             
18199             format : this.useIso ? 'Y-m-d' : this.format,
18200             minText : String.format(this.minText, this.formatDate(this.minValue)),
18201             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18202             
18203         });
18204          this.menu.on(Roo.apply({}, this.menuListeners, {
18205             scope:this
18206         }));
18207        
18208         
18209         var m = this.menu;
18210         var p = m.picker;
18211         
18212         // hide month picker get's called when we called by 'before hide';
18213         
18214         var ignorehide = true;
18215         p.hideMonthPicker  = function(disableAnim){
18216             if (ignorehide) {
18217                 return;
18218             }
18219              if(this.monthPicker){
18220                 Roo.log("hideMonthPicker called");
18221                 if(disableAnim === true){
18222                     this.monthPicker.hide();
18223                 }else{
18224                     this.monthPicker.slideOut('t', {duration:.2});
18225                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18226                     p.fireEvent("select", this, this.value);
18227                     m.hide();
18228                 }
18229             }
18230         }
18231         
18232         Roo.log('picker set value');
18233         Roo.log(this.getValue());
18234         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18235         m.show(this.el, 'tl-bl?');
18236         ignorehide  = false;
18237         // this will trigger hideMonthPicker..
18238         
18239         
18240         // hidden the day picker
18241         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18242         
18243         
18244         
18245       
18246         
18247         p.showMonthPicker.defer(100, p);
18248     
18249         
18250        
18251     },
18252
18253     beforeBlur : function(){
18254         var v = this.parseDate(this.getRawValue());
18255         if(v){
18256             this.setValue(v);
18257         }
18258     }
18259
18260     /** @cfg {Boolean} grow @hide */
18261     /** @cfg {Number} growMin @hide */
18262     /** @cfg {Number} growMax @hide */
18263     /**
18264      * @hide
18265      * @method autoSize
18266      */
18267 });/*
18268  * Based on:
18269  * Ext JS Library 1.1.1
18270  * Copyright(c) 2006-2007, Ext JS, LLC.
18271  *
18272  * Originally Released Under LGPL - original licence link has changed is not relivant.
18273  *
18274  * Fork - LGPL
18275  * <script type="text/javascript">
18276  */
18277  
18278
18279 /**
18280  * @class Roo.form.ComboBox
18281  * @extends Roo.form.TriggerField
18282  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18283  * @constructor
18284  * Create a new ComboBox.
18285  * @param {Object} config Configuration options
18286  */
18287 Roo.form.ComboBox = function(config){
18288     Roo.form.ComboBox.superclass.constructor.call(this, config);
18289     this.addEvents({
18290         /**
18291          * @event expand
18292          * Fires when the dropdown list is expanded
18293              * @param {Roo.form.ComboBox} combo This combo box
18294              */
18295         'expand' : true,
18296         /**
18297          * @event collapse
18298          * Fires when the dropdown list is collapsed
18299              * @param {Roo.form.ComboBox} combo This combo box
18300              */
18301         'collapse' : true,
18302         /**
18303          * @event beforeselect
18304          * Fires before a list item is selected. Return false to cancel the selection.
18305              * @param {Roo.form.ComboBox} combo This combo box
18306              * @param {Roo.data.Record} record The data record returned from the underlying store
18307              * @param {Number} index The index of the selected item in the dropdown list
18308              */
18309         'beforeselect' : true,
18310         /**
18311          * @event select
18312          * Fires when a list item is selected
18313              * @param {Roo.form.ComboBox} combo This combo box
18314              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18315              * @param {Number} index The index of the selected item in the dropdown list
18316              */
18317         'select' : true,
18318         /**
18319          * @event beforequery
18320          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18321          * The event object passed has these properties:
18322              * @param {Roo.form.ComboBox} combo This combo box
18323              * @param {String} query The query
18324              * @param {Boolean} forceAll true to force "all" query
18325              * @param {Boolean} cancel true to cancel the query
18326              * @param {Object} e The query event object
18327              */
18328         'beforequery': true,
18329          /**
18330          * @event add
18331          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18332              * @param {Roo.form.ComboBox} combo This combo box
18333              */
18334         'add' : true,
18335         /**
18336          * @event edit
18337          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18338              * @param {Roo.form.ComboBox} combo This combo box
18339              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18340              */
18341         'edit' : true
18342         
18343         
18344     });
18345     if(this.transform){
18346         this.allowDomMove = false;
18347         var s = Roo.getDom(this.transform);
18348         if(!this.hiddenName){
18349             this.hiddenName = s.name;
18350         }
18351         if(!this.store){
18352             this.mode = 'local';
18353             var d = [], opts = s.options;
18354             for(var i = 0, len = opts.length;i < len; i++){
18355                 var o = opts[i];
18356                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18357                 if(o.selected) {
18358                     this.value = value;
18359                 }
18360                 d.push([value, o.text]);
18361             }
18362             this.store = new Roo.data.SimpleStore({
18363                 'id': 0,
18364                 fields: ['value', 'text'],
18365                 data : d
18366             });
18367             this.valueField = 'value';
18368             this.displayField = 'text';
18369         }
18370         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18371         if(!this.lazyRender){
18372             this.target = true;
18373             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18374             s.parentNode.removeChild(s); // remove it
18375             this.render(this.el.parentNode);
18376         }else{
18377             s.parentNode.removeChild(s); // remove it
18378         }
18379
18380     }
18381     if (this.store) {
18382         this.store = Roo.factory(this.store, Roo.data);
18383     }
18384     
18385     this.selectedIndex = -1;
18386     if(this.mode == 'local'){
18387         if(config.queryDelay === undefined){
18388             this.queryDelay = 10;
18389         }
18390         if(config.minChars === undefined){
18391             this.minChars = 0;
18392         }
18393     }
18394 };
18395
18396 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18397     /**
18398      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18399      */
18400     /**
18401      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18402      * rendering into an Roo.Editor, defaults to false)
18403      */
18404     /**
18405      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18406      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18407      */
18408     /**
18409      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18410      */
18411     /**
18412      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18413      * the dropdown list (defaults to undefined, with no header element)
18414      */
18415
18416      /**
18417      * @cfg {String/Roo.Template} tpl The template to use to render the output
18418      */
18419      
18420     // private
18421     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18422     /**
18423      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18424      */
18425     listWidth: undefined,
18426     /**
18427      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18428      * mode = 'remote' or 'text' if mode = 'local')
18429      */
18430     displayField: undefined,
18431     /**
18432      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18433      * mode = 'remote' or 'value' if mode = 'local'). 
18434      * Note: use of a valueField requires the user make a selection
18435      * in order for a value to be mapped.
18436      */
18437     valueField: undefined,
18438     
18439     
18440     /**
18441      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18442      * field's data value (defaults to the underlying DOM element's name)
18443      */
18444     hiddenName: undefined,
18445     /**
18446      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18447      */
18448     listClass: '',
18449     /**
18450      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18451      */
18452     selectedClass: 'x-combo-selected',
18453     /**
18454      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18455      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18456      * which displays a downward arrow icon).
18457      */
18458     triggerClass : 'x-form-arrow-trigger',
18459     /**
18460      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18461      */
18462     shadow:'sides',
18463     /**
18464      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18465      * anchor positions (defaults to 'tl-bl')
18466      */
18467     listAlign: 'tl-bl?',
18468     /**
18469      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18470      */
18471     maxHeight: 300,
18472     /**
18473      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18474      * query specified by the allQuery config option (defaults to 'query')
18475      */
18476     triggerAction: 'query',
18477     /**
18478      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18479      * (defaults to 4, does not apply if editable = false)
18480      */
18481     minChars : 4,
18482     /**
18483      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18484      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18485      */
18486     typeAhead: false,
18487     /**
18488      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18489      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18490      */
18491     queryDelay: 500,
18492     /**
18493      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18494      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18495      */
18496     pageSize: 0,
18497     /**
18498      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18499      * when editable = true (defaults to false)
18500      */
18501     selectOnFocus:false,
18502     /**
18503      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18504      */
18505     queryParam: 'query',
18506     /**
18507      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18508      * when mode = 'remote' (defaults to 'Loading...')
18509      */
18510     loadingText: 'Loading...',
18511     /**
18512      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18513      */
18514     resizable: false,
18515     /**
18516      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18517      */
18518     handleHeight : 8,
18519     /**
18520      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18521      * traditional select (defaults to true)
18522      */
18523     editable: true,
18524     /**
18525      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18526      */
18527     allQuery: '',
18528     /**
18529      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18530      */
18531     mode: 'remote',
18532     /**
18533      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18534      * listWidth has a higher value)
18535      */
18536     minListWidth : 70,
18537     /**
18538      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18539      * allow the user to set arbitrary text into the field (defaults to false)
18540      */
18541     forceSelection:false,
18542     /**
18543      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18544      * if typeAhead = true (defaults to 250)
18545      */
18546     typeAheadDelay : 250,
18547     /**
18548      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18549      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18550      */
18551     valueNotFoundText : undefined,
18552     /**
18553      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18554      */
18555     blockFocus : false,
18556     
18557     /**
18558      * @cfg {Boolean} disableClear Disable showing of clear button.
18559      */
18560     disableClear : false,
18561     /**
18562      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18563      */
18564     alwaysQuery : false,
18565     
18566     //private
18567     addicon : false,
18568     editicon: false,
18569     
18570     // element that contains real text value.. (when hidden is used..)
18571      
18572     // private
18573     onRender : function(ct, position)
18574     {
18575         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18576         
18577         if(this.hiddenName){
18578             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18579                     'before', true);
18580             this.hiddenField.value =
18581                 this.hiddenValue !== undefined ? this.hiddenValue :
18582                 this.value !== undefined ? this.value : '';
18583
18584             // prevent input submission
18585             this.el.dom.removeAttribute('name');
18586              
18587              
18588         }
18589         
18590         if(Roo.isGecko){
18591             this.el.dom.setAttribute('autocomplete', 'off');
18592         }
18593
18594         var cls = 'x-combo-list';
18595
18596         this.list = new Roo.Layer({
18597             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18598         });
18599
18600         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18601         this.list.setWidth(lw);
18602         this.list.swallowEvent('mousewheel');
18603         this.assetHeight = 0;
18604
18605         if(this.title){
18606             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18607             this.assetHeight += this.header.getHeight();
18608         }
18609
18610         this.innerList = this.list.createChild({cls:cls+'-inner'});
18611         this.innerList.on('mouseover', this.onViewOver, this);
18612         this.innerList.on('mousemove', this.onViewMove, this);
18613         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18614         
18615         if(this.allowBlank && !this.pageSize && !this.disableClear){
18616             this.footer = this.list.createChild({cls:cls+'-ft'});
18617             this.pageTb = new Roo.Toolbar(this.footer);
18618            
18619         }
18620         if(this.pageSize){
18621             this.footer = this.list.createChild({cls:cls+'-ft'});
18622             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18623                     {pageSize: this.pageSize});
18624             
18625         }
18626         
18627         if (this.pageTb && this.allowBlank && !this.disableClear) {
18628             var _this = this;
18629             this.pageTb.add(new Roo.Toolbar.Fill(), {
18630                 cls: 'x-btn-icon x-btn-clear',
18631                 text: '&#160;',
18632                 handler: function()
18633                 {
18634                     _this.collapse();
18635                     _this.clearValue();
18636                     _this.onSelect(false, -1);
18637                 }
18638             });
18639         }
18640         if (this.footer) {
18641             this.assetHeight += this.footer.getHeight();
18642         }
18643         
18644
18645         if(!this.tpl){
18646             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18647         }
18648
18649         this.view = new Roo.View(this.innerList, this.tpl, {
18650             singleSelect:true,
18651             store: this.store,
18652             selectedClass: this.selectedClass
18653         });
18654
18655         this.view.on('click', this.onViewClick, this);
18656
18657         this.store.on('beforeload', this.onBeforeLoad, this);
18658         this.store.on('load', this.onLoad, this);
18659         this.store.on('loadexception', this.onLoadException, this);
18660
18661         if(this.resizable){
18662             this.resizer = new Roo.Resizable(this.list,  {
18663                pinned:true, handles:'se'
18664             });
18665             this.resizer.on('resize', function(r, w, h){
18666                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18667                 this.listWidth = w;
18668                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18669                 this.restrictHeight();
18670             }, this);
18671             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18672         }
18673         if(!this.editable){
18674             this.editable = true;
18675             this.setEditable(false);
18676         }  
18677         
18678         
18679         if (typeof(this.events.add.listeners) != 'undefined') {
18680             
18681             this.addicon = this.wrap.createChild(
18682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18683        
18684             this.addicon.on('click', function(e) {
18685                 this.fireEvent('add', this);
18686             }, this);
18687         }
18688         if (typeof(this.events.edit.listeners) != 'undefined') {
18689             
18690             this.editicon = this.wrap.createChild(
18691                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18692             if (this.addicon) {
18693                 this.editicon.setStyle('margin-left', '40px');
18694             }
18695             this.editicon.on('click', function(e) {
18696                 
18697                 // we fire even  if inothing is selected..
18698                 this.fireEvent('edit', this, this.lastData );
18699                 
18700             }, this);
18701         }
18702         
18703         
18704         
18705     },
18706
18707     // private
18708     initEvents : function(){
18709         Roo.form.ComboBox.superclass.initEvents.call(this);
18710
18711         this.keyNav = new Roo.KeyNav(this.el, {
18712             "up" : function(e){
18713                 this.inKeyMode = true;
18714                 this.selectPrev();
18715             },
18716
18717             "down" : function(e){
18718                 if(!this.isExpanded()){
18719                     this.onTriggerClick();
18720                 }else{
18721                     this.inKeyMode = true;
18722                     this.selectNext();
18723                 }
18724             },
18725
18726             "enter" : function(e){
18727                 this.onViewClick();
18728                 //return true;
18729             },
18730
18731             "esc" : function(e){
18732                 this.collapse();
18733             },
18734
18735             "tab" : function(e){
18736                 this.onViewClick(false);
18737                 this.fireEvent("specialkey", this, e);
18738                 return true;
18739             },
18740
18741             scope : this,
18742
18743             doRelay : function(foo, bar, hname){
18744                 if(hname == 'down' || this.scope.isExpanded()){
18745                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18746                 }
18747                 return true;
18748             },
18749
18750             forceKeyDown: true
18751         });
18752         this.queryDelay = Math.max(this.queryDelay || 10,
18753                 this.mode == 'local' ? 10 : 250);
18754         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18755         if(this.typeAhead){
18756             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18757         }
18758         if(this.editable !== false){
18759             this.el.on("keyup", this.onKeyUp, this);
18760         }
18761         if(this.forceSelection){
18762             this.on('blur', this.doForce, this);
18763         }
18764     },
18765
18766     onDestroy : function(){
18767         if(this.view){
18768             this.view.setStore(null);
18769             this.view.el.removeAllListeners();
18770             this.view.el.remove();
18771             this.view.purgeListeners();
18772         }
18773         if(this.list){
18774             this.list.destroy();
18775         }
18776         if(this.store){
18777             this.store.un('beforeload', this.onBeforeLoad, this);
18778             this.store.un('load', this.onLoad, this);
18779             this.store.un('loadexception', this.onLoadException, this);
18780         }
18781         Roo.form.ComboBox.superclass.onDestroy.call(this);
18782     },
18783
18784     // private
18785     fireKey : function(e){
18786         if(e.isNavKeyPress() && !this.list.isVisible()){
18787             this.fireEvent("specialkey", this, e);
18788         }
18789     },
18790
18791     // private
18792     onResize: function(w, h){
18793         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18794         
18795         if(typeof w != 'number'){
18796             // we do not handle it!?!?
18797             return;
18798         }
18799         var tw = this.trigger.getWidth();
18800         tw += this.addicon ? this.addicon.getWidth() : 0;
18801         tw += this.editicon ? this.editicon.getWidth() : 0;
18802         var x = w - tw;
18803         this.el.setWidth( this.adjustWidth('input', x));
18804             
18805         this.trigger.setStyle('left', x+'px');
18806         
18807         if(this.list && this.listWidth === undefined){
18808             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18809             this.list.setWidth(lw);
18810             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18811         }
18812         
18813     
18814         
18815     },
18816
18817     /**
18818      * Allow or prevent the user from directly editing the field text.  If false is passed,
18819      * the user will only be able to select from the items defined in the dropdown list.  This method
18820      * is the runtime equivalent of setting the 'editable' config option at config time.
18821      * @param {Boolean} value True to allow the user to directly edit the field text
18822      */
18823     setEditable : function(value){
18824         if(value == this.editable){
18825             return;
18826         }
18827         this.editable = value;
18828         if(!value){
18829             this.el.dom.setAttribute('readOnly', true);
18830             this.el.on('mousedown', this.onTriggerClick,  this);
18831             this.el.addClass('x-combo-noedit');
18832         }else{
18833             this.el.dom.setAttribute('readOnly', false);
18834             this.el.un('mousedown', this.onTriggerClick,  this);
18835             this.el.removeClass('x-combo-noedit');
18836         }
18837     },
18838
18839     // private
18840     onBeforeLoad : function(){
18841         if(!this.hasFocus){
18842             return;
18843         }
18844         this.innerList.update(this.loadingText ?
18845                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18846         this.restrictHeight();
18847         this.selectedIndex = -1;
18848     },
18849
18850     // private
18851     onLoad : function(){
18852         if(!this.hasFocus){
18853             return;
18854         }
18855         if(this.store.getCount() > 0){
18856             this.expand();
18857             this.restrictHeight();
18858             if(this.lastQuery == this.allQuery){
18859                 if(this.editable){
18860                     this.el.dom.select();
18861                 }
18862                 if(!this.selectByValue(this.value, true)){
18863                     this.select(0, true);
18864                 }
18865             }else{
18866                 this.selectNext();
18867                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18868                     this.taTask.delay(this.typeAheadDelay);
18869                 }
18870             }
18871         }else{
18872             this.onEmptyResults();
18873         }
18874         //this.el.focus();
18875     },
18876     // private
18877     onLoadException : function()
18878     {
18879         this.collapse();
18880         Roo.log(this.store.reader.jsonData);
18881         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18882             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18883         }
18884         
18885         
18886     },
18887     // private
18888     onTypeAhead : function(){
18889         if(this.store.getCount() > 0){
18890             var r = this.store.getAt(0);
18891             var newValue = r.data[this.displayField];
18892             var len = newValue.length;
18893             var selStart = this.getRawValue().length;
18894             if(selStart != len){
18895                 this.setRawValue(newValue);
18896                 this.selectText(selStart, newValue.length);
18897             }
18898         }
18899     },
18900
18901     // private
18902     onSelect : function(record, index){
18903         if(this.fireEvent('beforeselect', this, record, index) !== false){
18904             this.setFromData(index > -1 ? record.data : false);
18905             this.collapse();
18906             this.fireEvent('select', this, record, index);
18907         }
18908     },
18909
18910     /**
18911      * Returns the currently selected field value or empty string if no value is set.
18912      * @return {String} value The selected value
18913      */
18914     getValue : function(){
18915         if(this.valueField){
18916             return typeof this.value != 'undefined' ? this.value : '';
18917         }
18918         return Roo.form.ComboBox.superclass.getValue.call(this);
18919     },
18920
18921     /**
18922      * Clears any text/value currently set in the field
18923      */
18924     clearValue : function(){
18925         if(this.hiddenField){
18926             this.hiddenField.value = '';
18927         }
18928         this.value = '';
18929         this.setRawValue('');
18930         this.lastSelectionText = '';
18931         
18932     },
18933
18934     /**
18935      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18936      * will be displayed in the field.  If the value does not match the data value of an existing item,
18937      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18938      * Otherwise the field will be blank (although the value will still be set).
18939      * @param {String} value The value to match
18940      */
18941     setValue : function(v){
18942         var text = v;
18943         if(this.valueField){
18944             var r = this.findRecord(this.valueField, v);
18945             if(r){
18946                 text = r.data[this.displayField];
18947             }else if(this.valueNotFoundText !== undefined){
18948                 text = this.valueNotFoundText;
18949             }
18950         }
18951         this.lastSelectionText = text;
18952         if(this.hiddenField){
18953             this.hiddenField.value = v;
18954         }
18955         Roo.form.ComboBox.superclass.setValue.call(this, text);
18956         this.value = v;
18957     },
18958     /**
18959      * @property {Object} the last set data for the element
18960      */
18961     
18962     lastData : false,
18963     /**
18964      * Sets the value of the field based on a object which is related to the record format for the store.
18965      * @param {Object} value the value to set as. or false on reset?
18966      */
18967     setFromData : function(o){
18968         var dv = ''; // display value
18969         var vv = ''; // value value..
18970         this.lastData = o;
18971         if (this.displayField) {
18972             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18973         } else {
18974             // this is an error condition!!!
18975             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18976         }
18977         
18978         if(this.valueField){
18979             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18980         }
18981         if(this.hiddenField){
18982             this.hiddenField.value = vv;
18983             
18984             this.lastSelectionText = dv;
18985             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18986             this.value = vv;
18987             return;
18988         }
18989         // no hidden field.. - we store the value in 'value', but still display
18990         // display field!!!!
18991         this.lastSelectionText = dv;
18992         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18993         this.value = vv;
18994         
18995         
18996     },
18997     // private
18998     reset : function(){
18999         // overridden so that last data is reset..
19000         this.setValue(this.resetValue);
19001         this.originalValue = this.getValue();
19002         this.clearInvalid();
19003         this.lastData = false;
19004         if (this.view) {
19005             this.view.clearSelections();
19006         }
19007     },
19008     // private
19009     findRecord : function(prop, value){
19010         var record;
19011         if(this.store.getCount() > 0){
19012             this.store.each(function(r){
19013                 if(r.data[prop] == value){
19014                     record = r;
19015                     return false;
19016                 }
19017                 return true;
19018             });
19019         }
19020         return record;
19021     },
19022     
19023     getName: function()
19024     {
19025         // returns hidden if it's set..
19026         if (!this.rendered) {return ''};
19027         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19028         
19029     },
19030     // private
19031     onViewMove : function(e, t){
19032         this.inKeyMode = false;
19033     },
19034
19035     // private
19036     onViewOver : function(e, t){
19037         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19038             return;
19039         }
19040         var item = this.view.findItemFromChild(t);
19041         if(item){
19042             var index = this.view.indexOf(item);
19043             this.select(index, false);
19044         }
19045     },
19046
19047     // private
19048     onViewClick : function(doFocus)
19049     {
19050         var index = this.view.getSelectedIndexes()[0];
19051         var r = this.store.getAt(index);
19052         if(r){
19053             this.onSelect(r, index);
19054         }
19055         if(doFocus !== false && !this.blockFocus){
19056             this.el.focus();
19057         }
19058     },
19059
19060     // private
19061     restrictHeight : function(){
19062         this.innerList.dom.style.height = '';
19063         var inner = this.innerList.dom;
19064         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19065         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19066         this.list.beginUpdate();
19067         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19068         this.list.alignTo(this.el, this.listAlign);
19069         this.list.endUpdate();
19070     },
19071
19072     // private
19073     onEmptyResults : function(){
19074         this.collapse();
19075     },
19076
19077     /**
19078      * Returns true if the dropdown list is expanded, else false.
19079      */
19080     isExpanded : function(){
19081         return this.list.isVisible();
19082     },
19083
19084     /**
19085      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19086      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19087      * @param {String} value The data value of the item to select
19088      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19089      * selected item if it is not currently in view (defaults to true)
19090      * @return {Boolean} True if the value matched an item in the list, else false
19091      */
19092     selectByValue : function(v, scrollIntoView){
19093         if(v !== undefined && v !== null){
19094             var r = this.findRecord(this.valueField || this.displayField, v);
19095             if(r){
19096                 this.select(this.store.indexOf(r), scrollIntoView);
19097                 return true;
19098             }
19099         }
19100         return false;
19101     },
19102
19103     /**
19104      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19105      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19106      * @param {Number} index The zero-based index of the list item to select
19107      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19108      * selected item if it is not currently in view (defaults to true)
19109      */
19110     select : function(index, scrollIntoView){
19111         this.selectedIndex = index;
19112         this.view.select(index);
19113         if(scrollIntoView !== false){
19114             var el = this.view.getNode(index);
19115             if(el){
19116                 this.innerList.scrollChildIntoView(el, false);
19117             }
19118         }
19119     },
19120
19121     // private
19122     selectNext : function(){
19123         var ct = this.store.getCount();
19124         if(ct > 0){
19125             if(this.selectedIndex == -1){
19126                 this.select(0);
19127             }else if(this.selectedIndex < ct-1){
19128                 this.select(this.selectedIndex+1);
19129             }
19130         }
19131     },
19132
19133     // private
19134     selectPrev : function(){
19135         var ct = this.store.getCount();
19136         if(ct > 0){
19137             if(this.selectedIndex == -1){
19138                 this.select(0);
19139             }else if(this.selectedIndex != 0){
19140                 this.select(this.selectedIndex-1);
19141             }
19142         }
19143     },
19144
19145     // private
19146     onKeyUp : function(e){
19147         if(this.editable !== false && !e.isSpecialKey()){
19148             this.lastKey = e.getKey();
19149             this.dqTask.delay(this.queryDelay);
19150         }
19151     },
19152
19153     // private
19154     validateBlur : function(){
19155         return !this.list || !this.list.isVisible();   
19156     },
19157
19158     // private
19159     initQuery : function(){
19160         this.doQuery(this.getRawValue());
19161     },
19162
19163     // private
19164     doForce : function(){
19165         if(this.el.dom.value.length > 0){
19166             this.el.dom.value =
19167                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19168              
19169         }
19170     },
19171
19172     /**
19173      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19174      * query allowing the query action to be canceled if needed.
19175      * @param {String} query The SQL query to execute
19176      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19177      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19178      * saved in the current store (defaults to false)
19179      */
19180     doQuery : function(q, forceAll){
19181         if(q === undefined || q === null){
19182             q = '';
19183         }
19184         var qe = {
19185             query: q,
19186             forceAll: forceAll,
19187             combo: this,
19188             cancel:false
19189         };
19190         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19191             return false;
19192         }
19193         q = qe.query;
19194         forceAll = qe.forceAll;
19195         if(forceAll === true || (q.length >= this.minChars)){
19196             if(this.lastQuery != q || this.alwaysQuery){
19197                 this.lastQuery = q;
19198                 if(this.mode == 'local'){
19199                     this.selectedIndex = -1;
19200                     if(forceAll){
19201                         this.store.clearFilter();
19202                     }else{
19203                         this.store.filter(this.displayField, q);
19204                     }
19205                     this.onLoad();
19206                 }else{
19207                     this.store.baseParams[this.queryParam] = q;
19208                     this.store.load({
19209                         params: this.getParams(q)
19210                     });
19211                     this.expand();
19212                 }
19213             }else{
19214                 this.selectedIndex = -1;
19215                 this.onLoad();   
19216             }
19217         }
19218     },
19219
19220     // private
19221     getParams : function(q){
19222         var p = {};
19223         //p[this.queryParam] = q;
19224         if(this.pageSize){
19225             p.start = 0;
19226             p.limit = this.pageSize;
19227         }
19228         return p;
19229     },
19230
19231     /**
19232      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19233      */
19234     collapse : function(){
19235         if(!this.isExpanded()){
19236             return;
19237         }
19238         this.list.hide();
19239         Roo.get(document).un('mousedown', this.collapseIf, this);
19240         Roo.get(document).un('mousewheel', this.collapseIf, this);
19241         if (!this.editable) {
19242             Roo.get(document).un('keydown', this.listKeyPress, this);
19243         }
19244         this.fireEvent('collapse', this);
19245     },
19246
19247     // private
19248     collapseIf : function(e){
19249         if(!e.within(this.wrap) && !e.within(this.list)){
19250             this.collapse();
19251         }
19252     },
19253
19254     /**
19255      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19256      */
19257     expand : function(){
19258         if(this.isExpanded() || !this.hasFocus){
19259             return;
19260         }
19261         this.list.alignTo(this.el, this.listAlign);
19262         this.list.show();
19263         Roo.get(document).on('mousedown', this.collapseIf, this);
19264         Roo.get(document).on('mousewheel', this.collapseIf, this);
19265         if (!this.editable) {
19266             Roo.get(document).on('keydown', this.listKeyPress, this);
19267         }
19268         
19269         this.fireEvent('expand', this);
19270     },
19271
19272     // private
19273     // Implements the default empty TriggerField.onTriggerClick function
19274     onTriggerClick : function(){
19275         if(this.disabled){
19276             return;
19277         }
19278         if(this.isExpanded()){
19279             this.collapse();
19280             if (!this.blockFocus) {
19281                 this.el.focus();
19282             }
19283             
19284         }else {
19285             this.hasFocus = true;
19286             if(this.triggerAction == 'all') {
19287                 this.doQuery(this.allQuery, true);
19288             } else {
19289                 this.doQuery(this.getRawValue());
19290             }
19291             if (!this.blockFocus) {
19292                 this.el.focus();
19293             }
19294         }
19295     },
19296     listKeyPress : function(e)
19297     {
19298         //Roo.log('listkeypress');
19299         // scroll to first matching element based on key pres..
19300         if (e.isSpecialKey()) {
19301             return false;
19302         }
19303         var k = String.fromCharCode(e.getKey()).toUpperCase();
19304         //Roo.log(k);
19305         var match  = false;
19306         var csel = this.view.getSelectedNodes();
19307         var cselitem = false;
19308         if (csel.length) {
19309             var ix = this.view.indexOf(csel[0]);
19310             cselitem  = this.store.getAt(ix);
19311             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19312                 cselitem = false;
19313             }
19314             
19315         }
19316         
19317         this.store.each(function(v) { 
19318             if (cselitem) {
19319                 // start at existing selection.
19320                 if (cselitem.id == v.id) {
19321                     cselitem = false;
19322                 }
19323                 return;
19324             }
19325                 
19326             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19327                 match = this.store.indexOf(v);
19328                 return false;
19329             }
19330         }, this);
19331         
19332         if (match === false) {
19333             return true; // no more action?
19334         }
19335         // scroll to?
19336         this.view.select(match);
19337         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19338         sn.scrollIntoView(sn.dom.parentNode, false);
19339     } 
19340
19341     /** 
19342     * @cfg {Boolean} grow 
19343     * @hide 
19344     */
19345     /** 
19346     * @cfg {Number} growMin 
19347     * @hide 
19348     */
19349     /** 
19350     * @cfg {Number} growMax 
19351     * @hide 
19352     */
19353     /**
19354      * @hide
19355      * @method autoSize
19356      */
19357 });/*
19358  * Copyright(c) 2010-2012, Roo J Solutions Limited
19359  *
19360  * Licence LGPL
19361  *
19362  */
19363
19364 /**
19365  * @class Roo.form.ComboBoxArray
19366  * @extends Roo.form.TextField
19367  * A facebook style adder... for lists of email / people / countries  etc...
19368  * pick multiple items from a combo box, and shows each one.
19369  *
19370  *  Fred [x]  Brian [x]  [Pick another |v]
19371  *
19372  *
19373  *  For this to work: it needs various extra information
19374  *    - normal combo problay has
19375  *      name, hiddenName
19376  *    + displayField, valueField
19377  *
19378  *    For our purpose...
19379  *
19380  *
19381  *   If we change from 'extends' to wrapping...
19382  *   
19383  *  
19384  *
19385  
19386  
19387  * @constructor
19388  * Create a new ComboBoxArray.
19389  * @param {Object} config Configuration options
19390  */
19391  
19392
19393 Roo.form.ComboBoxArray = function(config)
19394 {
19395     this.addEvents({
19396         /**
19397          * @event beforeremove
19398          * Fires before remove the value from the list
19399              * @param {Roo.form.ComboBoxArray} _self This combo box array
19400              * @param {Roo.form.ComboBoxArray.Item} item removed item
19401              */
19402         'beforeremove' : true,
19403         /**
19404          * @event remove
19405          * Fires when remove the value from the list
19406              * @param {Roo.form.ComboBoxArray} _self This combo box array
19407              * @param {Roo.form.ComboBoxArray.Item} item removed item
19408              */
19409         'remove' : true
19410         
19411         
19412     });
19413     
19414     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19415     
19416     this.items = new Roo.util.MixedCollection(false);
19417     
19418     // construct the child combo...
19419     
19420     
19421     
19422     
19423    
19424     
19425 }
19426
19427  
19428 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19429
19430     /**
19431      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19432      */
19433     
19434     lastData : false,
19435     
19436     // behavies liek a hiddne field
19437     inputType:      'hidden',
19438     /**
19439      * @cfg {Number} width The width of the box that displays the selected element
19440      */ 
19441     width:          300,
19442
19443     
19444     
19445     /**
19446      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19447      */
19448     name : false,
19449     /**
19450      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19451      */
19452     hiddenName : false,
19453       /**
19454      * @cfg {String} seperator    The value seperator normally ',' 
19455      */
19456     seperator : ',',
19457     
19458     // private the array of items that are displayed..
19459     items  : false,
19460     // private - the hidden field el.
19461     hiddenEl : false,
19462     // private - the filed el..
19463     el : false,
19464     
19465     //validateValue : function() { return true; }, // all values are ok!
19466     //onAddClick: function() { },
19467     
19468     onRender : function(ct, position) 
19469     {
19470         
19471         // create the standard hidden element
19472         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19473         
19474         
19475         // give fake names to child combo;
19476         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19477         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19478         
19479         this.combo = Roo.factory(this.combo, Roo.form);
19480         this.combo.onRender(ct, position);
19481         if (typeof(this.combo.width) != 'undefined') {
19482             this.combo.onResize(this.combo.width,0);
19483         }
19484         
19485         this.combo.initEvents();
19486         
19487         // assigned so form know we need to do this..
19488         this.store          = this.combo.store;
19489         this.valueField     = this.combo.valueField;
19490         this.displayField   = this.combo.displayField ;
19491         
19492         
19493         this.combo.wrap.addClass('x-cbarray-grp');
19494         
19495         var cbwrap = this.combo.wrap.createChild(
19496             {tag: 'div', cls: 'x-cbarray-cb'},
19497             this.combo.el.dom
19498         );
19499         
19500              
19501         this.hiddenEl = this.combo.wrap.createChild({
19502             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19503         });
19504         this.el = this.combo.wrap.createChild({
19505             tag: 'input',  type:'hidden' , name: this.name, value : ''
19506         });
19507          //   this.el.dom.removeAttribute("name");
19508         
19509         
19510         this.outerWrap = this.combo.wrap;
19511         this.wrap = cbwrap;
19512         
19513         this.outerWrap.setWidth(this.width);
19514         this.outerWrap.dom.removeChild(this.el.dom);
19515         
19516         this.wrap.dom.appendChild(this.el.dom);
19517         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19518         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19519         
19520         this.combo.trigger.setStyle('position','relative');
19521         this.combo.trigger.setStyle('left', '0px');
19522         this.combo.trigger.setStyle('top', '2px');
19523         
19524         this.combo.el.setStyle('vertical-align', 'text-bottom');
19525         
19526         //this.trigger.setStyle('vertical-align', 'top');
19527         
19528         // this should use the code from combo really... on('add' ....)
19529         if (this.adder) {
19530             
19531         
19532             this.adder = this.outerWrap.createChild(
19533                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19534             var _t = this;
19535             this.adder.on('click', function(e) {
19536                 _t.fireEvent('adderclick', this, e);
19537             }, _t);
19538         }
19539         //var _t = this;
19540         //this.adder.on('click', this.onAddClick, _t);
19541         
19542         
19543         this.combo.on('select', function(cb, rec, ix) {
19544             this.addItem(rec.data);
19545             
19546             cb.setValue('');
19547             cb.el.dom.value = '';
19548             //cb.lastData = rec.data;
19549             // add to list
19550             
19551         }, this);
19552         
19553         
19554     },
19555     
19556     
19557     getName: function()
19558     {
19559         // returns hidden if it's set..
19560         if (!this.rendered) {return ''};
19561         return  this.hiddenName ? this.hiddenName : this.name;
19562         
19563     },
19564     
19565     
19566     onResize: function(w, h){
19567         
19568         return;
19569         // not sure if this is needed..
19570         //this.combo.onResize(w,h);
19571         
19572         if(typeof w != 'number'){
19573             // we do not handle it!?!?
19574             return;
19575         }
19576         var tw = this.combo.trigger.getWidth();
19577         tw += this.addicon ? this.addicon.getWidth() : 0;
19578         tw += this.editicon ? this.editicon.getWidth() : 0;
19579         var x = w - tw;
19580         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19581             
19582         this.combo.trigger.setStyle('left', '0px');
19583         
19584         if(this.list && this.listWidth === undefined){
19585             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19586             this.list.setWidth(lw);
19587             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19588         }
19589         
19590     
19591         
19592     },
19593     
19594     addItem: function(rec)
19595     {
19596         var valueField = this.combo.valueField;
19597         var displayField = this.combo.displayField;
19598         
19599         if (this.items.indexOfKey(rec[valueField]) > -1) {
19600             //console.log("GOT " + rec.data.id);
19601             return;
19602         }
19603         
19604         var x = new Roo.form.ComboBoxArray.Item({
19605             //id : rec[this.idField],
19606             data : rec,
19607             displayField : displayField ,
19608             tipField : displayField ,
19609             cb : this
19610         });
19611         // use the 
19612         this.items.add(rec[valueField],x);
19613         // add it before the element..
19614         this.updateHiddenEl();
19615         x.render(this.outerWrap, this.wrap.dom);
19616         // add the image handler..
19617     },
19618     
19619     updateHiddenEl : function()
19620     {
19621         this.validate();
19622         if (!this.hiddenEl) {
19623             return;
19624         }
19625         var ar = [];
19626         var idField = this.combo.valueField;
19627         
19628         this.items.each(function(f) {
19629             ar.push(f.data[idField]);
19630         });
19631         this.hiddenEl.dom.value = ar.join(this.seperator);
19632         this.validate();
19633     },
19634     
19635     reset : function()
19636     {
19637         this.items.clear();
19638         
19639         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19640            el.remove();
19641         });
19642         
19643         this.el.dom.value = '';
19644         if (this.hiddenEl) {
19645             this.hiddenEl.dom.value = '';
19646         }
19647         
19648     },
19649     getValue: function()
19650     {
19651         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19652     },
19653     setValue: function(v) // not a valid action - must use addItems..
19654     {
19655         
19656         this.reset();
19657          
19658         if (this.store.isLocal && (typeof(v) == 'string')) {
19659             // then we can use the store to find the values..
19660             // comma seperated at present.. this needs to allow JSON based encoding..
19661             this.hiddenEl.value  = v;
19662             var v_ar = [];
19663             Roo.each(v.split(this.seperator), function(k) {
19664                 Roo.log("CHECK " + this.valueField + ',' + k);
19665                 var li = this.store.query(this.valueField, k);
19666                 if (!li.length) {
19667                     return;
19668                 }
19669                 var add = {};
19670                 add[this.valueField] = k;
19671                 add[this.displayField] = li.item(0).data[this.displayField];
19672                 
19673                 this.addItem(add);
19674             }, this) 
19675              
19676         }
19677         if (typeof(v) == 'object' ) {
19678             // then let's assume it's an array of objects..
19679             Roo.each(v, function(l) {
19680                 var add = l;
19681                 if (typeof(l) == 'string') {
19682                     add = {};
19683                     add[this.valueField] = l;
19684                     add[this.displayField] = l
19685                 }
19686                 this.addItem(add);
19687             }, this);
19688              
19689         }
19690         
19691         
19692     },
19693     setFromData: function(v)
19694     {
19695         // this recieves an object, if setValues is called.
19696         this.reset();
19697         this.el.dom.value = v[this.displayField];
19698         this.hiddenEl.dom.value = v[this.valueField];
19699         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19700             return;
19701         }
19702         var kv = v[this.valueField];
19703         var dv = v[this.displayField];
19704         kv = typeof(kv) != 'string' ? '' : kv;
19705         dv = typeof(dv) != 'string' ? '' : dv;
19706         
19707         
19708         var keys = kv.split(this.seperator);
19709         var display = dv.split(this.seperator);
19710         for (var i = 0 ; i < keys.length; i++) {
19711             add = {};
19712             add[this.valueField] = keys[i];
19713             add[this.displayField] = display[i];
19714             this.addItem(add);
19715         }
19716       
19717         
19718     },
19719     
19720     /**
19721      * Validates the combox array value
19722      * @return {Boolean} True if the value is valid, else false
19723      */
19724     validate : function(){
19725         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19726             this.clearInvalid();
19727             return true;
19728         }
19729         return false;
19730     },
19731     
19732     validateValue : function(value){
19733         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19734         
19735     },
19736     
19737     /*@
19738      * overide
19739      * 
19740      */
19741     isDirty : function() {
19742         if(this.disabled) {
19743             return false;
19744         }
19745         
19746         try {
19747             var d = Roo.decode(String(this.originalValue));
19748         } catch (e) {
19749             return String(this.getValue()) !== String(this.originalValue);
19750         }
19751         
19752         var originalValue = [];
19753         
19754         for (var i = 0; i < d.length; i++){
19755             originalValue.push(d[i][this.valueField]);
19756         }
19757         
19758         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19759         
19760     }
19761     
19762 });
19763
19764
19765
19766 /**
19767  * @class Roo.form.ComboBoxArray.Item
19768  * @extends Roo.BoxComponent
19769  * A selected item in the list
19770  *  Fred [x]  Brian [x]  [Pick another |v]
19771  * 
19772  * @constructor
19773  * Create a new item.
19774  * @param {Object} config Configuration options
19775  */
19776  
19777 Roo.form.ComboBoxArray.Item = function(config) {
19778     config.id = Roo.id();
19779     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19780 }
19781
19782 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19783     data : {},
19784     cb: false,
19785     displayField : false,
19786     tipField : false,
19787     
19788     
19789     defaultAutoCreate : {
19790         tag: 'div',
19791         cls: 'x-cbarray-item',
19792         cn : [ 
19793             { tag: 'div' },
19794             {
19795                 tag: 'img',
19796                 width:16,
19797                 height : 16,
19798                 src : Roo.BLANK_IMAGE_URL ,
19799                 align: 'center'
19800             }
19801         ]
19802         
19803     },
19804     
19805  
19806     onRender : function(ct, position)
19807     {
19808         Roo.form.Field.superclass.onRender.call(this, ct, position);
19809         
19810         if(!this.el){
19811             var cfg = this.getAutoCreate();
19812             this.el = ct.createChild(cfg, position);
19813         }
19814         
19815         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19816         
19817         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19818             this.cb.renderer(this.data) :
19819             String.format('{0}',this.data[this.displayField]);
19820         
19821             
19822         this.el.child('div').dom.setAttribute('qtip',
19823                         String.format('{0}',this.data[this.tipField])
19824         );
19825         
19826         this.el.child('img').on('click', this.remove, this);
19827         
19828     },
19829    
19830     remove : function()
19831     {
19832         if(this.cb.disabled){
19833             return;
19834         }
19835         
19836         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19837             this.cb.items.remove(this);
19838             this.el.child('img').un('click', this.remove, this);
19839             this.el.remove();
19840             this.cb.updateHiddenEl();
19841
19842             this.cb.fireEvent('remove', this.cb, this);
19843         }
19844         
19845     }
19846 });/*
19847  * RooJS Library 1.1.1
19848  * Copyright(c) 2008-2011  Alan Knowles
19849  *
19850  * License - LGPL
19851  */
19852  
19853
19854 /**
19855  * @class Roo.form.ComboNested
19856  * @extends Roo.form.ComboBox
19857  * A combobox for that allows selection of nested items in a list,
19858  * eg.
19859  *
19860  *  Book
19861  *    -> red
19862  *    -> green
19863  *  Table
19864  *    -> square
19865  *      ->red
19866  *      ->green
19867  *    -> rectangle
19868  *      ->green
19869  *      
19870  * 
19871  * @constructor
19872  * Create a new ComboNested
19873  * @param {Object} config Configuration options
19874  */
19875 Roo.form.ComboNested = function(config){
19876     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19877     // should verify some data...
19878     // like
19879     // hiddenName = required..
19880     // displayField = required
19881     // valudField == required
19882     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19883     var _t = this;
19884     Roo.each(req, function(e) {
19885         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19886             throw "Roo.form.ComboNested : missing value for: " + e;
19887         }
19888     });
19889      
19890     
19891 };
19892
19893 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19894    
19895     /*
19896      * @config {Number} max Number of columns to show
19897      */
19898     
19899     maxColumns : 3,
19900    
19901     list : null, // the outermost div..
19902     innerLists : null, // the
19903     views : null,
19904     stores : null,
19905     // private
19906     loadingChildren : false,
19907     
19908     onRender : function(ct, position)
19909     {
19910         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19911         
19912         if(this.hiddenName){
19913             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19914                     'before', true);
19915             this.hiddenField.value =
19916                 this.hiddenValue !== undefined ? this.hiddenValue :
19917                 this.value !== undefined ? this.value : '';
19918
19919             // prevent input submission
19920             this.el.dom.removeAttribute('name');
19921              
19922              
19923         }
19924         
19925         if(Roo.isGecko){
19926             this.el.dom.setAttribute('autocomplete', 'off');
19927         }
19928
19929         var cls = 'x-combo-list';
19930
19931         this.list = new Roo.Layer({
19932             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19933         });
19934
19935         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19936         this.list.setWidth(lw);
19937         this.list.swallowEvent('mousewheel');
19938         this.assetHeight = 0;
19939
19940         if(this.title){
19941             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19942             this.assetHeight += this.header.getHeight();
19943         }
19944         this.innerLists = [];
19945         this.views = [];
19946         this.stores = [];
19947         for (var i =0 ; i < this.maxColumns; i++) {
19948             this.onRenderList( cls, i);
19949         }
19950         
19951         // always needs footer, as we are going to have an 'OK' button.
19952         this.footer = this.list.createChild({cls:cls+'-ft'});
19953         this.pageTb = new Roo.Toolbar(this.footer);  
19954         var _this = this;
19955         this.pageTb.add(  {
19956             
19957             text: 'Done',
19958             handler: function()
19959             {
19960                 _this.collapse();
19961             }
19962         });
19963         
19964         if ( this.allowBlank && !this.disableClear) {
19965             
19966             this.pageTb.add(new Roo.Toolbar.Fill(), {
19967                 cls: 'x-btn-icon x-btn-clear',
19968                 text: '&#160;',
19969                 handler: function()
19970                 {
19971                     _this.collapse();
19972                     _this.clearValue();
19973                     _this.onSelect(false, -1);
19974                 }
19975             });
19976         }
19977         if (this.footer) {
19978             this.assetHeight += this.footer.getHeight();
19979         }
19980         
19981     },
19982     onRenderList : function (  cls, i)
19983     {
19984         
19985         var lw = Math.floor(
19986                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19987         );
19988         
19989         this.list.setWidth(lw); // default to '1'
19990
19991         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19992         //il.on('mouseover', this.onViewOver, this, { list:  i });
19993         //il.on('mousemove', this.onViewMove, this, { list:  i });
19994         il.setWidth(lw);
19995         il.setStyle({ 'overflow-x' : 'hidden'});
19996
19997         if(!this.tpl){
19998             this.tpl = new Roo.Template({
19999                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20000                 isEmpty: function (value, allValues) {
20001                     //Roo.log(value);
20002                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20003                     return dl ? 'has-children' : 'no-children'
20004                 }
20005             });
20006         }
20007         
20008         var store  = this.store;
20009         if (i > 0) {
20010             store  = new Roo.data.SimpleStore({
20011                 //fields : this.store.reader.meta.fields,
20012                 reader : this.store.reader,
20013                 data : [ ]
20014             });
20015         }
20016         this.stores[i]  = store;
20017                   
20018         var view = this.views[i] = new Roo.View(
20019             il,
20020             this.tpl,
20021             {
20022                 singleSelect:true,
20023                 store: store,
20024                 selectedClass: this.selectedClass
20025             }
20026         );
20027         view.getEl().setWidth(lw);
20028         view.getEl().setStyle({
20029             position: i < 1 ? 'relative' : 'absolute',
20030             top: 0,
20031             left: (i * lw ) + 'px',
20032             display : i > 0 ? 'none' : 'block'
20033         });
20034         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20035         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20036         //view.on('click', this.onViewClick, this, { list : i });
20037
20038         store.on('beforeload', this.onBeforeLoad, this);
20039         store.on('load',  this.onLoad, this, { list  : i});
20040         store.on('loadexception', this.onLoadException, this);
20041
20042         // hide the other vies..
20043         
20044         
20045         
20046     },
20047       
20048     restrictHeight : function()
20049     {
20050         var mh = 0;
20051         Roo.each(this.innerLists, function(il,i) {
20052             var el = this.views[i].getEl();
20053             el.dom.style.height = '';
20054             var inner = el.dom;
20055             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20056             // only adjust heights on other ones..
20057             mh = Math.max(h, mh);
20058             if (i < 1) {
20059                 
20060                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20061                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20062                
20063             }
20064             
20065             
20066         }, this);
20067         
20068         this.list.beginUpdate();
20069         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20070         this.list.alignTo(this.el, this.listAlign);
20071         this.list.endUpdate();
20072         
20073     },
20074      
20075     
20076     // -- store handlers..
20077     // private
20078     onBeforeLoad : function()
20079     {
20080         if(!this.hasFocus){
20081             return;
20082         }
20083         this.innerLists[0].update(this.loadingText ?
20084                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20085         this.restrictHeight();
20086         this.selectedIndex = -1;
20087     },
20088     // private
20089     onLoad : function(a,b,c,d)
20090     {
20091         if (!this.loadingChildren) {
20092             // then we are loading the top level. - hide the children
20093             for (var i = 1;i < this.views.length; i++) {
20094                 this.views[i].getEl().setStyle({ display : 'none' });
20095             }
20096             var lw = Math.floor(
20097                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20098             );
20099         
20100              this.list.setWidth(lw); // default to '1'
20101
20102             
20103         }
20104         if(!this.hasFocus){
20105             return;
20106         }
20107         
20108         if(this.store.getCount() > 0) {
20109             this.expand();
20110             this.restrictHeight();   
20111         } else {
20112             this.onEmptyResults();
20113         }
20114         
20115         if (!this.loadingChildren) {
20116             this.selectActive();
20117         }
20118         /*
20119         this.stores[1].loadData([]);
20120         this.stores[2].loadData([]);
20121         this.views
20122         */    
20123     
20124         //this.el.focus();
20125     },
20126     
20127     
20128     // private
20129     onLoadException : function()
20130     {
20131         this.collapse();
20132         Roo.log(this.store.reader.jsonData);
20133         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20134             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20135         }
20136         
20137         
20138     },
20139     // no cleaning of leading spaces on blur here.
20140     cleanLeadingSpace : function(e) { },
20141     
20142
20143     onSelectChange : function (view, sels, opts )
20144     {
20145         var ix = view.getSelectedIndexes();
20146          
20147         if (opts.list > this.maxColumns - 2) {
20148             if (view.store.getCount()<  1) {
20149                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20150
20151             } else  {
20152                 if (ix.length) {
20153                     // used to clear ?? but if we are loading unselected 
20154                     this.setFromData(view.store.getAt(ix[0]).data);
20155                 }
20156                 
20157             }
20158             
20159             return;
20160         }
20161         
20162         if (!ix.length) {
20163             // this get's fired when trigger opens..
20164            // this.setFromData({});
20165             var str = this.stores[opts.list+1];
20166             str.data.clear(); // removeall wihtout the fire events..
20167             return;
20168         }
20169         
20170         var rec = view.store.getAt(ix[0]);
20171          
20172         this.setFromData(rec.data);
20173         this.fireEvent('select', this, rec, ix[0]);
20174         
20175         var lw = Math.floor(
20176              (
20177                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20178              ) / this.maxColumns
20179         );
20180         this.loadingChildren = true;
20181         this.stores[opts.list+1].loadDataFromChildren( rec );
20182         this.loadingChildren = false;
20183         var dl = this.stores[opts.list+1]. getTotalCount();
20184         
20185         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20186         
20187         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20188         for (var i = opts.list+2; i < this.views.length;i++) {
20189             this.views[i].getEl().setStyle({ display : 'none' });
20190         }
20191         
20192         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20193         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20194         
20195         if (this.isLoading) {
20196            // this.selectActive(opts.list);
20197         }
20198          
20199     },
20200     
20201     
20202     
20203     
20204     onDoubleClick : function()
20205     {
20206         this.collapse(); //??
20207     },
20208     
20209      
20210     
20211     
20212     
20213     // private
20214     recordToStack : function(store, prop, value, stack)
20215     {
20216         var cstore = new Roo.data.SimpleStore({
20217             //fields : this.store.reader.meta.fields, // we need array reader.. for
20218             reader : this.store.reader,
20219             data : [ ]
20220         });
20221         var _this = this;
20222         var record  = false;
20223         var srec = false;
20224         if(store.getCount() < 1){
20225             return false;
20226         }
20227         store.each(function(r){
20228             if(r.data[prop] == value){
20229                 record = r;
20230             srec = r;
20231                 return false;
20232             }
20233             if (r.data.cn && r.data.cn.length) {
20234                 cstore.loadDataFromChildren( r);
20235                 var cret = _this.recordToStack(cstore, prop, value, stack);
20236                 if (cret !== false) {
20237                     record = cret;
20238                     srec = r;
20239                     return false;
20240                 }
20241             }
20242              
20243             return true;
20244         });
20245         if (record == false) {
20246             return false
20247         }
20248         stack.unshift(srec);
20249         return record;
20250     },
20251     
20252     /*
20253      * find the stack of stores that match our value.
20254      *
20255      * 
20256      */
20257     
20258     selectActive : function ()
20259     {
20260         // if store is not loaded, then we will need to wait for that to happen first.
20261         var stack = [];
20262         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20263         for (var i = 0; i < stack.length; i++ ) {
20264             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20265         }
20266         
20267     }
20268         
20269          
20270     
20271     
20272     
20273     
20274 });/*
20275  * Based on:
20276  * Ext JS Library 1.1.1
20277  * Copyright(c) 2006-2007, Ext JS, LLC.
20278  *
20279  * Originally Released Under LGPL - original licence link has changed is not relivant.
20280  *
20281  * Fork - LGPL
20282  * <script type="text/javascript">
20283  */
20284 /**
20285  * @class Roo.form.Checkbox
20286  * @extends Roo.form.Field
20287  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20288  * @constructor
20289  * Creates a new Checkbox
20290  * @param {Object} config Configuration options
20291  */
20292 Roo.form.Checkbox = function(config){
20293     Roo.form.Checkbox.superclass.constructor.call(this, config);
20294     this.addEvents({
20295         /**
20296          * @event check
20297          * Fires when the checkbox is checked or unchecked.
20298              * @param {Roo.form.Checkbox} this This checkbox
20299              * @param {Boolean} checked The new checked value
20300              */
20301         check : true
20302     });
20303 };
20304
20305 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20306     /**
20307      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20308      */
20309     focusClass : undefined,
20310     /**
20311      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20312      */
20313     fieldClass: "x-form-field",
20314     /**
20315      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20316      */
20317     checked: false,
20318     /**
20319      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20320      * {tag: "input", type: "checkbox", autocomplete: "off"})
20321      */
20322     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20323     /**
20324      * @cfg {String} boxLabel The text that appears beside the checkbox
20325      */
20326     boxLabel : "",
20327     /**
20328      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20329      */  
20330     inputValue : '1',
20331     /**
20332      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20333      */
20334      valueOff: '0', // value when not checked..
20335
20336     actionMode : 'viewEl', 
20337     //
20338     // private
20339     itemCls : 'x-menu-check-item x-form-item',
20340     groupClass : 'x-menu-group-item',
20341     inputType : 'hidden',
20342     
20343     
20344     inSetChecked: false, // check that we are not calling self...
20345     
20346     inputElement: false, // real input element?
20347     basedOn: false, // ????
20348     
20349     isFormField: true, // not sure where this is needed!!!!
20350
20351     onResize : function(){
20352         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20353         if(!this.boxLabel){
20354             this.el.alignTo(this.wrap, 'c-c');
20355         }
20356     },
20357
20358     initEvents : function(){
20359         Roo.form.Checkbox.superclass.initEvents.call(this);
20360         this.el.on("click", this.onClick,  this);
20361         this.el.on("change", this.onClick,  this);
20362     },
20363
20364
20365     getResizeEl : function(){
20366         return this.wrap;
20367     },
20368
20369     getPositionEl : function(){
20370         return this.wrap;
20371     },
20372
20373     // private
20374     onRender : function(ct, position){
20375         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20376         /*
20377         if(this.inputValue !== undefined){
20378             this.el.dom.value = this.inputValue;
20379         }
20380         */
20381         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20382         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20383         var viewEl = this.wrap.createChild({ 
20384             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20385         this.viewEl = viewEl;   
20386         this.wrap.on('click', this.onClick,  this); 
20387         
20388         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20389         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20390         
20391         
20392         
20393         if(this.boxLabel){
20394             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20395         //    viewEl.on('click', this.onClick,  this); 
20396         }
20397         //if(this.checked){
20398             this.setChecked(this.checked);
20399         //}else{
20400             //this.checked = this.el.dom;
20401         //}
20402
20403     },
20404
20405     // private
20406     initValue : Roo.emptyFn,
20407
20408     /**
20409      * Returns the checked state of the checkbox.
20410      * @return {Boolean} True if checked, else false
20411      */
20412     getValue : function(){
20413         if(this.el){
20414             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20415         }
20416         return this.valueOff;
20417         
20418     },
20419
20420         // private
20421     onClick : function(){ 
20422         if (this.disabled) {
20423             return;
20424         }
20425         this.setChecked(!this.checked);
20426
20427         //if(this.el.dom.checked != this.checked){
20428         //    this.setValue(this.el.dom.checked);
20429        // }
20430     },
20431
20432     /**
20433      * Sets the checked state of the checkbox.
20434      * On is always based on a string comparison between inputValue and the param.
20435      * @param {Boolean/String} value - the value to set 
20436      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20437      */
20438     setValue : function(v,suppressEvent){
20439         
20440         
20441         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20442         //if(this.el && this.el.dom){
20443         //    this.el.dom.checked = this.checked;
20444         //    this.el.dom.defaultChecked = this.checked;
20445         //}
20446         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20447         //this.fireEvent("check", this, this.checked);
20448     },
20449     // private..
20450     setChecked : function(state,suppressEvent)
20451     {
20452         if (this.inSetChecked) {
20453             this.checked = state;
20454             return;
20455         }
20456         
20457     
20458         if(this.wrap){
20459             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20460         }
20461         this.checked = state;
20462         if(suppressEvent !== true){
20463             this.fireEvent('check', this, state);
20464         }
20465         this.inSetChecked = true;
20466         this.el.dom.value = state ? this.inputValue : this.valueOff;
20467         this.inSetChecked = false;
20468         
20469     },
20470     // handle setting of hidden value by some other method!!?!?
20471     setFromHidden: function()
20472     {
20473         if(!this.el){
20474             return;
20475         }
20476         //console.log("SET FROM HIDDEN");
20477         //alert('setFrom hidden');
20478         this.setValue(this.el.dom.value);
20479     },
20480     
20481     onDestroy : function()
20482     {
20483         if(this.viewEl){
20484             Roo.get(this.viewEl).remove();
20485         }
20486          
20487         Roo.form.Checkbox.superclass.onDestroy.call(this);
20488     },
20489     
20490     setBoxLabel : function(str)
20491     {
20492         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20493     }
20494
20495 });/*
20496  * Based on:
20497  * Ext JS Library 1.1.1
20498  * Copyright(c) 2006-2007, Ext JS, LLC.
20499  *
20500  * Originally Released Under LGPL - original licence link has changed is not relivant.
20501  *
20502  * Fork - LGPL
20503  * <script type="text/javascript">
20504  */
20505  
20506 /**
20507  * @class Roo.form.Radio
20508  * @extends Roo.form.Checkbox
20509  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20510  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20511  * @constructor
20512  * Creates a new Radio
20513  * @param {Object} config Configuration options
20514  */
20515 Roo.form.Radio = function(){
20516     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20517 };
20518 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20519     inputType: 'radio',
20520
20521     /**
20522      * If this radio is part of a group, it will return the selected value
20523      * @return {String}
20524      */
20525     getGroupValue : function(){
20526         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20527     },
20528     
20529     
20530     onRender : function(ct, position){
20531         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20532         
20533         if(this.inputValue !== undefined){
20534             this.el.dom.value = this.inputValue;
20535         }
20536          
20537         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20538         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20539         //var viewEl = this.wrap.createChild({ 
20540         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20541         //this.viewEl = viewEl;   
20542         //this.wrap.on('click', this.onClick,  this); 
20543         
20544         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20545         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20546         
20547         
20548         
20549         if(this.boxLabel){
20550             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20551         //    viewEl.on('click', this.onClick,  this); 
20552         }
20553          if(this.checked){
20554             this.el.dom.checked =   'checked' ;
20555         }
20556          
20557     } 
20558     
20559     
20560 });Roo.rtf = {}; // namespace
20561 Roo.rtf.Hex = function(hex)
20562 {
20563     this.hexstr = hex;
20564 };
20565 Roo.rtf.Paragraph = function(opts)
20566 {
20567     this.content = []; ///??? is that used?
20568 };Roo.rtf.Span = function(opts)
20569 {
20570     this.value = opts.value;
20571 };
20572
20573 Roo.rtf.Group = function(parent)
20574 {
20575     // we dont want to acutally store parent - it will make debug a nightmare..
20576     this.content = [];
20577     this.cn  = [];
20578      
20579        
20580     
20581 };
20582
20583 Roo.rtf.Group.prototype = {
20584     ignorable : false,
20585     content: false,
20586     cn: false,
20587     addContent : function(node) {
20588         // could set styles...
20589         this.content.push(node);
20590     },
20591     addChild : function(cn)
20592     {
20593         this.cn.push(cn);
20594     },
20595     // only for images really...
20596     toDataURL : function()
20597     {
20598         var mimetype = false;
20599         switch(true) {
20600             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
20601                 mimetype = "image/png";
20602                 break;
20603              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20604                 mimetype = "image/jpeg";
20605                 break;
20606             default :
20607                 return 'about:blank'; // ?? error?
20608         }
20609         
20610         
20611         var hexstring = this.content[this.content.length-1].value;
20612         
20613         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20614             return String.fromCharCode(parseInt(a, 16));
20615         }).join(""));
20616     }
20617     
20618 };
20619 // this looks like it's normally the {rtf{ .... }}
20620 Roo.rtf.Document = function()
20621 {
20622     // we dont want to acutally store parent - it will make debug a nightmare..
20623     this.rtlch  = [];
20624     this.content = [];
20625     this.cn = [];
20626     
20627 };
20628 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
20629     addChild : function(cn)
20630     {
20631         this.cn.push(cn);
20632         switch(cn.type) {
20633             case 'rtlch': // most content seems to be inside this??
20634             case 'listtext':
20635             case 'shpinst':
20636                 this.rtlch.push(cn);
20637                 return;
20638             default:
20639                 this[cn.type] = cn;
20640         }
20641         
20642     },
20643     
20644     getElementsByType : function(type)
20645     {
20646         var ret =  [];
20647         this._getElementsByType(type, ret, this.cn, 'rtf');
20648         return ret;
20649     },
20650     _getElementsByType : function (type, ret, search_array, path)
20651     {
20652         search_array.forEach(function(n,i) {
20653             if (n.type == type) {
20654                 n.path = path + '/' + n.type + ':' + i;
20655                 ret.push(n);
20656             }
20657             if (n.cn.length > 0) {
20658                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20659             }
20660         },this);
20661     }
20662     
20663 });
20664  
20665 Roo.rtf.Ctrl = function(opts)
20666 {
20667     this.value = opts.value;
20668     this.param = opts.param;
20669 };
20670 /**
20671  *
20672  *
20673  * based on this https://github.com/iarna/rtf-parser
20674  * it's really only designed to extract pict from pasted RTF 
20675  *
20676  * usage:
20677  *
20678  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20679  *  
20680  *
20681  */
20682
20683  
20684
20685
20686
20687 Roo.rtf.Parser = function(text) {
20688     //super({objectMode: true})
20689     this.text = '';
20690     this.parserState = this.parseText;
20691     
20692     // these are for interpeter...
20693     this.doc = {};
20694     ///this.parserState = this.parseTop
20695     this.groupStack = [];
20696     this.hexStore = [];
20697     this.doc = false;
20698     
20699     this.groups = []; // where we put the return.
20700     
20701     for (var ii = 0; ii < text.length; ++ii) {
20702         ++this.cpos;
20703         
20704         if (text[ii] === '\n') {
20705             ++this.row;
20706             this.col = 1;
20707         } else {
20708             ++this.col;
20709         }
20710         this.parserState(text[ii]);
20711     }
20712     
20713     
20714     
20715 };
20716 Roo.rtf.Parser.prototype = {
20717     text : '', // string being parsed..
20718     controlWord : '',
20719     controlWordParam :  '',
20720     hexChar : '',
20721     doc : false,
20722     group: false,
20723     groupStack : false,
20724     hexStore : false,
20725     
20726     
20727     cpos : 0, 
20728     row : 1, // reportin?
20729     col : 1, //
20730
20731      
20732     push : function (el)
20733     {
20734         var m = 'cmd'+ el.type;
20735         if (typeof(this[m]) == 'undefined') {
20736             Roo.log('invalid cmd:' + el.type);
20737             return;
20738         }
20739         this[m](el);
20740         //Roo.log(el);
20741     },
20742     flushHexStore : function()
20743     {
20744         if (this.hexStore.length < 1) {
20745             return;
20746         }
20747         var hexstr = this.hexStore.map(
20748             function(cmd) {
20749                 return cmd.value;
20750         }).join('');
20751         
20752         this.group.addContent( new Roo.rtf.Hex( hexstr ));
20753               
20754             
20755         this.hexStore.splice(0)
20756         
20757     },
20758     
20759     cmdgroupstart : function()
20760     {
20761         this.flushHexStore();
20762         if (this.group) {
20763             this.groupStack.push(this.group);
20764         }
20765          // parent..
20766         if (this.doc === false) {
20767             this.group = this.doc = new Roo.rtf.Document();
20768             return;
20769             
20770         }
20771         this.group = new Roo.rtf.Group(this.group);
20772     },
20773     cmdignorable : function()
20774     {
20775         this.flushHexStore();
20776         this.group.ignorable = true;
20777     },
20778     cmdendparagraph : function()
20779     {
20780         this.flushHexStore();
20781         this.group.addContent(new Roo.rtf.Paragraph());
20782     },
20783     cmdgroupend : function ()
20784     {
20785         this.flushHexStore();
20786         var endingGroup = this.group;
20787         
20788         
20789         this.group = this.groupStack.pop();
20790         if (this.group) {
20791             this.group.addChild(endingGroup);
20792         }
20793         
20794         
20795         
20796         var doc = this.group || this.doc;
20797         //if (endingGroup instanceof FontTable) {
20798         //  doc.fonts = endingGroup.table
20799         //} else if (endingGroup instanceof ColorTable) {
20800         //  doc.colors = endingGroup.table
20801         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20802         if (endingGroup.ignorable === false) {
20803             //code
20804             this.groups.push(endingGroup);
20805            // Roo.log( endingGroup );
20806         }
20807             //Roo.each(endingGroup.content, function(item)) {
20808             //    doc.addContent(item);
20809             //}
20810             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20811         //}
20812     },
20813     cmdtext : function (cmd)
20814     {
20815         this.flushHexStore();
20816         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20817             //this.group = this.doc
20818         }
20819         this.group.addContent(new Roo.rtf.Span(cmd));
20820     },
20821     cmdcontrolword : function (cmd)
20822     {
20823         this.flushHexStore();
20824         if (!this.group.type) {
20825             this.group.type = cmd.value;
20826             return;
20827         }
20828         this.group.addContent(new Roo.rtf.Ctrl(cmd));
20829         // we actually don't care about ctrl words...
20830         return ;
20831         /*
20832         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20833         if (this[method]) {
20834             this[method](cmd.param)
20835         } else {
20836             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20837         }
20838         */
20839     },
20840     cmdhexchar : function(cmd) {
20841         this.hexStore.push(cmd);
20842     },
20843     cmderror : function(cmd) {
20844         throw new Exception (cmd.value);
20845     },
20846     
20847     /*
20848       _flush (done) {
20849         if (this.text !== '\u0000') this.emitText()
20850         done()
20851       }
20852       */
20853       
20854       
20855     parseText : function(c)
20856     {
20857         if (c === '\\') {
20858             this.parserState = this.parseEscapes;
20859         } else if (c === '{') {
20860             this.emitStartGroup();
20861         } else if (c === '}') {
20862             this.emitEndGroup();
20863         } else if (c === '\x0A' || c === '\x0D') {
20864             // cr/lf are noise chars
20865         } else {
20866             this.text += c;
20867         }
20868     },
20869     
20870     parseEscapes: function (c)
20871     {
20872         if (c === '\\' || c === '{' || c === '}') {
20873             this.text += c;
20874             this.parserState = this.parseText;
20875         } else {
20876             this.parserState = this.parseControlSymbol;
20877             this.parseControlSymbol(c);
20878         }
20879     },
20880     parseControlSymbol: function(c)
20881     {
20882         if (c === '~') {
20883             this.text += '\u00a0'; // nbsp
20884             this.parserState = this.parseText
20885         } else if (c === '-') {
20886              this.text += '\u00ad'; // soft hyphen
20887         } else if (c === '_') {
20888             this.text += '\u2011'; // non-breaking hyphen
20889         } else if (c === '*') {
20890             this.emitIgnorable();
20891             this.parserState = this.parseText;
20892         } else if (c === "'") {
20893             this.parserState = this.parseHexChar;
20894         } else if (c === '|') { // formula cacter
20895             this.emitFormula();
20896             this.parserState = this.parseText;
20897         } else if (c === ':') { // subentry in an index entry
20898             this.emitIndexSubEntry();
20899             this.parserState = this.parseText;
20900         } else if (c === '\x0a') {
20901             this.emitEndParagraph();
20902             this.parserState = this.parseText;
20903         } else if (c === '\x0d') {
20904             this.emitEndParagraph();
20905             this.parserState = this.parseText;
20906         } else {
20907             this.parserState = this.parseControlWord;
20908             this.parseControlWord(c);
20909         }
20910     },
20911     parseHexChar: function (c)
20912     {
20913         if (/^[A-Fa-f0-9]$/.test(c)) {
20914             this.hexChar += c;
20915             if (this.hexChar.length >= 2) {
20916               this.emitHexChar();
20917               this.parserState = this.parseText;
20918             }
20919             return;
20920         }
20921         this.emitError("Invalid character \"" + c + "\" in hex literal.");
20922         this.parserState = this.parseText;
20923         
20924     },
20925     parseControlWord : function(c)
20926     {
20927         if (c === ' ') {
20928             this.emitControlWord();
20929             this.parserState = this.parseText;
20930         } else if (/^[-\d]$/.test(c)) {
20931             this.parserState = this.parseControlWordParam;
20932             this.controlWordParam += c;
20933         } else if (/^[A-Za-z]$/.test(c)) {
20934           this.controlWord += c;
20935         } else {
20936           this.emitControlWord();
20937           this.parserState = this.parseText;
20938           this.parseText(c);
20939         }
20940     },
20941     parseControlWordParam : function (c) {
20942         if (/^\d$/.test(c)) {
20943           this.controlWordParam += c;
20944         } else if (c === ' ') {
20945           this.emitControlWord();
20946           this.parserState = this.parseText;
20947         } else {
20948           this.emitControlWord();
20949           this.parserState = this.parseText;
20950           this.parseText(c);
20951         }
20952     },
20953     
20954     
20955     
20956     
20957     emitText : function () {
20958         if (this.text === '') {
20959             return;
20960         }
20961         this.push({
20962             type: 'text',
20963             value: this.text,
20964             pos: this.cpos,
20965             row: this.row,
20966             col: this.col
20967         });
20968         this.text = ''
20969     },
20970     emitControlWord : function ()
20971     {
20972         this.emitText();
20973         if (this.controlWord === '') {
20974             this.emitError('empty control word');
20975         } else {
20976             this.push({
20977                   type: 'controlword',
20978                   value: this.controlWord,
20979                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
20980                   pos: this.cpos,
20981                   row: this.row,
20982                   col: this.col
20983             });
20984         }
20985         this.controlWord = '';
20986         this.controlWordParam = '';
20987     },
20988     emitStartGroup : function ()
20989     {
20990         this.emitText();
20991         this.push({
20992             type: 'groupstart',
20993             pos: this.cpos,
20994             row: this.row,
20995             col: this.col
20996         });
20997     },
20998     emitEndGroup : function ()
20999     {
21000         this.emitText();
21001         this.push({
21002             type: 'groupend',
21003             pos: this.cpos,
21004             row: this.row,
21005             col: this.col
21006         });
21007     },
21008     emitIgnorable : function ()
21009     {
21010         this.emitText();
21011         this.push({
21012             type: 'ignorable',
21013             pos: this.cpos,
21014             row: this.row,
21015             col: this.col
21016         });
21017     },
21018     emitHexChar : function ()
21019     {
21020         this.emitText();
21021         this.push({
21022             type: 'hexchar',
21023             value: this.hexChar,
21024             pos: this.cpos,
21025             row: this.row,
21026             col: this.col
21027         });
21028         this.hexChar = ''
21029     },
21030     emitError : function (message)
21031     {
21032       this.emitText();
21033       this.push({
21034             type: 'error',
21035             value: message,
21036             row: this.row,
21037             col: this.col,
21038             char: this.cpos //,
21039             //stack: new Error().stack
21040         });
21041     },
21042     emitEndParagraph : function () {
21043         this.emitText();
21044         this.push({
21045             type: 'endparagraph',
21046             pos: this.cpos,
21047             row: this.row,
21048             col: this.col
21049         });
21050     }
21051      
21052 } ;
21053 Roo.htmleditor = {};
21054  
21055 /**
21056  * @class Roo.htmleditor.Filter
21057  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21058  * @cfg {DomElement} node The node to iterate and filter
21059  * @cfg {boolean|String|Array} tag Tags to replace 
21060  * @constructor
21061  * Create a new Filter.
21062  * @param {Object} config Configuration options
21063  */
21064
21065
21066
21067 Roo.htmleditor.Filter = function(cfg) {
21068     Roo.apply(this.cfg);
21069     // this does not actually call walk as it's really just a abstract class
21070 }
21071
21072
21073 Roo.htmleditor.Filter.prototype = {
21074     
21075     node: false,
21076     
21077     tag: false,
21078
21079     // overrride to do replace comments.
21080     replaceComment : false,
21081     
21082     // overrride to do replace or do stuff with tags..
21083     replaceTag : false,
21084     
21085     walk : function(dom)
21086     {
21087         Roo.each( Array.from(dom.childNodes), function( e ) {
21088             switch(true) {
21089                 
21090                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
21091                     this.replaceComment(e);
21092                     return;
21093                 
21094                 case e.nodeType != 1: //not a node.
21095                     return;
21096                 
21097                 case this.tag === true: // everything
21098                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21099                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21100                     if (this.replaceTag && false === this.replaceTag(e)) {
21101                         return;
21102                     }
21103                     if (e.hasChildNodes()) {
21104                         this.walk(e);
21105                     }
21106                     return;
21107                 
21108                 default:    // tags .. that do not match.
21109                     if (e.hasChildNodes()) {
21110                         this.walk(e);
21111                     }
21112             }
21113             
21114         }, this);
21115         
21116     }
21117 }; 
21118
21119 /**
21120  * @class Roo.htmleditor.FilterAttributes
21121  * clean attributes and  styles including http:// etc.. in attribute
21122  * @constructor
21123 * Run a new Attribute Filter
21124 * @param {Object} config Configuration options
21125  */
21126 Roo.htmleditor.FilterAttributes = function(cfg)
21127 {
21128     Roo.apply(this, cfg);
21129     this.attrib_black = this.attrib_black || [];
21130     this.attrib_white = this.attrib_white || [];
21131
21132     this.attrib_clean = this.attrib_clean || [];
21133     this.style_white = this.style_white || [];
21134     this.style_black = this.style_black || [];
21135     this.walk(cfg.node);
21136 }
21137
21138 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21139 {
21140     tag: true, // all tags
21141     
21142     attrib_black : false, // array
21143     attrib_clean : false,
21144     attrib_white : false,
21145
21146     style_white : false,
21147     style_black : false,
21148      
21149      
21150     replaceTag : function(node)
21151     {
21152         if (!node.attributes || !node.attributes.length) {
21153             return true;
21154         }
21155         
21156         for (var i = node.attributes.length-1; i > -1 ; i--) {
21157             var a = node.attributes[i];
21158             //console.log(a);
21159             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21160                 node.removeAttribute(a.name);
21161                 continue;
21162             }
21163             
21164             
21165             
21166             if (a.name.toLowerCase().substr(0,2)=='on')  {
21167                 node.removeAttribute(a.name);
21168                 continue;
21169             }
21170             
21171             
21172             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21173                 node.removeAttribute(a.name);
21174                 continue;
21175             }
21176             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21177                 this.cleanAttr(node,a.name,a.value); // fixme..
21178                 continue;
21179             }
21180             if (a.name == 'style') {
21181                 this.cleanStyle(node,a.name,a.value);
21182                 continue;
21183             }
21184             /// clean up MS crap..
21185             // tecnically this should be a list of valid class'es..
21186             
21187             
21188             if (a.name == 'class') {
21189                 if (a.value.match(/^Mso/)) {
21190                     node.removeAttribute('class');
21191                 }
21192                 
21193                 if (a.value.match(/^body$/)) {
21194                     node.removeAttribute('class');
21195                 }
21196                 continue;
21197             }
21198             
21199             
21200             // style cleanup!?
21201             // class cleanup?
21202             
21203         }
21204         return true; // clean children
21205     },
21206         
21207     cleanAttr: function(node, n,v)
21208     {
21209         
21210         if (v.match(/^\./) || v.match(/^\//)) {
21211             return;
21212         }
21213         if (v.match(/^(http|https):\/\//)
21214             || v.match(/^mailto:/) 
21215             || v.match(/^ftp:/)
21216             || v.match(/^data:/)
21217             ) {
21218             return;
21219         }
21220         if (v.match(/^#/)) {
21221             return;
21222         }
21223         if (v.match(/^\{/)) { // allow template editing.
21224             return;
21225         }
21226 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21227         node.removeAttribute(n);
21228         
21229     },
21230     cleanStyle : function(node,  n,v)
21231     {
21232         if (v.match(/expression/)) { //XSS?? should we even bother..
21233             node.removeAttribute(n);
21234             return;
21235         }
21236         
21237         var parts = v.split(/;/);
21238         var clean = [];
21239         
21240         Roo.each(parts, function(p) {
21241             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21242             if (!p.length) {
21243                 return true;
21244             }
21245             var l = p.split(':').shift().replace(/\s+/g,'');
21246             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21247             
21248             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21249                 return true;
21250             }
21251             //Roo.log()
21252             // only allow 'c whitelisted system attributes'
21253             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21254                 return true;
21255             }
21256             
21257             
21258             clean.push(p);
21259             return true;
21260         },this);
21261         if (clean.length) { 
21262             node.setAttribute(n, clean.join(';'));
21263         } else {
21264             node.removeAttribute(n);
21265         }
21266         
21267     }
21268         
21269         
21270         
21271     
21272 });/**
21273  * @class Roo.htmleditor.FilterBlack
21274  * remove blacklisted elements.
21275  * @constructor
21276  * Run a new Blacklisted Filter
21277  * @param {Object} config Configuration options
21278  */
21279
21280 Roo.htmleditor.FilterBlack = function(cfg)
21281 {
21282     Roo.apply(this, cfg);
21283     this.walk(cfg.node);
21284 }
21285
21286 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21287 {
21288     tag : true, // all elements.
21289    
21290     replace : function(n)
21291     {
21292         n.parentNode.removeChild(n);
21293     }
21294 });
21295 /**
21296  * @class Roo.htmleditor.FilterComment
21297  * remove comments.
21298  * @constructor
21299 * Run a new Comments Filter
21300 * @param {Object} config Configuration options
21301  */
21302 Roo.htmleditor.FilterComment = function(cfg)
21303 {
21304     this.walk(cfg.node);
21305 }
21306
21307 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21308 {
21309   
21310     replaceComment : function(n)
21311     {
21312         n.parentNode.removeChild(n);
21313     }
21314 });/**
21315  * @class Roo.htmleditor.FilterKeepChildren
21316  * remove tags but keep children
21317  * @constructor
21318  * Run a new Keep Children Filter
21319  * @param {Object} config Configuration options
21320  */
21321
21322 Roo.htmleditor.FilterKeepChildren = function(cfg)
21323 {
21324     Roo.apply(this, cfg);
21325     if (this.tag === false) {
21326         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21327     }
21328     this.walk(cfg.node);
21329 }
21330
21331 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21332 {
21333     
21334   
21335     replaceTag : function(node)
21336     {
21337         // walk children...
21338         //Roo.log(node);
21339         var ar = Array.from(node.childNodes);
21340         //remove first..
21341         for (var i = 0; i < ar.length; i++) {
21342             if (ar[i].nodeType == 1) {
21343                 if (
21344                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21345                     || // array and it matches
21346                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21347                 ) {
21348                     this.replaceTag(ar[i]); // child is blacklisted as well...
21349                     continue;
21350                 }
21351             }
21352         }  
21353         ar = Array.from(node.childNodes);
21354         for (var i = 0; i < ar.length; i++) {
21355          
21356             node.removeChild(ar[i]);
21357             // what if we need to walk these???
21358             node.parentNode.insertBefore(ar[i], node);
21359             if (this.tag !== false) {
21360                 this.walk(ar[i]);
21361                 
21362             }
21363         }
21364         node.parentNode.removeChild(node);
21365         return false; // don't walk children
21366         
21367         
21368     }
21369 });/**
21370  * @class Roo.htmleditor.FilterParagraph
21371  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21372  * like on 'push' to remove the <p> tags and replace them with line breaks.
21373  * @constructor
21374  * Run a new Paragraph Filter
21375  * @param {Object} config Configuration options
21376  */
21377
21378 Roo.htmleditor.FilterParagraph = function(cfg)
21379 {
21380     // no need to apply config.
21381     this.walk(cfg.node);
21382 }
21383
21384 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21385 {
21386     
21387      
21388     tag : 'P',
21389     
21390      
21391     replaceTag : function(node)
21392     {
21393         
21394         if (node.childNodes.length == 1 &&
21395             node.childNodes[0].nodeType == 3 &&
21396             node.childNodes[0].textContent.trim().length < 1
21397             ) {
21398             // remove and replace with '<BR>';
21399             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21400             return false; // no need to walk..
21401         }
21402         var ar = Array.from(node.childNodes);
21403         for (var i = 0; i < ar.length; i++) {
21404             node.removeChild(ar[i]);
21405             // what if we need to walk these???
21406             node.parentNode.insertBefore(ar[i], node);
21407         }
21408         // now what about this?
21409         // <p> &nbsp; </p>
21410         
21411         // double BR.
21412         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21413         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21414         node.parentNode.removeChild(node);
21415         
21416         return false;
21417
21418     }
21419     
21420 });/**
21421  * @class Roo.htmleditor.FilterSpan
21422  * filter span's with no attributes out..
21423  * @constructor
21424  * Run a new Span Filter
21425  * @param {Object} config Configuration options
21426  */
21427
21428 Roo.htmleditor.FilterSpan = function(cfg)
21429 {
21430     // no need to apply config.
21431     this.walk(cfg.node);
21432 }
21433
21434 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21435 {
21436      
21437     tag : 'SPAN',
21438      
21439  
21440     replaceTag : function(node)
21441     {
21442         if (node.attributes && node.attributes.length > 0) {
21443             return true; // walk if there are any.
21444         }
21445         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21446         return false;
21447      
21448     }
21449     
21450 });/**
21451  * @class Roo.htmleditor.FilterTableWidth
21452   try and remove table width data - as that frequently messes up other stuff.
21453  * 
21454  *      was cleanTableWidths.
21455  *
21456  * Quite often pasting from word etc.. results in tables with column and widths.
21457  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21458  *
21459  * @constructor
21460  * Run a new Table Filter
21461  * @param {Object} config Configuration options
21462  */
21463
21464 Roo.htmleditor.FilterTableWidth = function(cfg)
21465 {
21466     // no need to apply config.
21467     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21468     this.walk(cfg.node);
21469 }
21470
21471 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21472 {
21473      
21474      
21475     
21476     replaceTag: function(node) {
21477         
21478         
21479       
21480         if (node.hasAttribute('width')) {
21481             node.removeAttribute('width');
21482         }
21483         
21484          
21485         if (node.hasAttribute("style")) {
21486             // pretty basic...
21487             
21488             var styles = node.getAttribute("style").split(";");
21489             var nstyle = [];
21490             Roo.each(styles, function(s) {
21491                 if (!s.match(/:/)) {
21492                     return;
21493                 }
21494                 var kv = s.split(":");
21495                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21496                     return;
21497                 }
21498                 // what ever is left... we allow.
21499                 nstyle.push(s);
21500             });
21501             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21502             if (!nstyle.length) {
21503                 node.removeAttribute('style');
21504             }
21505         }
21506         
21507         return true; // continue doing children..
21508     }
21509 });/**
21510  * @class Roo.htmleditor.FilterWord
21511  * try and clean up all the mess that Word generates.
21512  * 
21513  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
21514  
21515  * @constructor
21516  * Run a new Span Filter
21517  * @param {Object} config Configuration options
21518  */
21519
21520 Roo.htmleditor.FilterWord = function(cfg)
21521 {
21522     // no need to apply config.
21523     this.walk(cfg.node);
21524 }
21525
21526 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21527 {
21528     tag: true,
21529      
21530     
21531     /**
21532      * Clean up MS wordisms...
21533      */
21534     replaceTag : function(node)
21535     {
21536          
21537         // no idea what this does - span with text, replaceds with just text.
21538         if(
21539                 node.nodeName == 'SPAN' &&
21540                 !node.hasAttributes() &&
21541                 node.childNodes.length == 1 &&
21542                 node.firstChild.nodeName == "#text"  
21543         ) {
21544             var textNode = node.firstChild;
21545             node.removeChild(textNode);
21546             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21547                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21548             }
21549             node.parentNode.insertBefore(textNode, node);
21550             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21551                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21552             }
21553             
21554             node.parentNode.removeChild(node);
21555             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21556         }
21557         
21558    
21559         
21560         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21561             node.parentNode.removeChild(node);
21562             return false; // dont do chidlren
21563         }
21564         //Roo.log(node.tagName);
21565         // remove - but keep children..
21566         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21567             //Roo.log('-- removed');
21568             while (node.childNodes.length) {
21569                 var cn = node.childNodes[0];
21570                 node.removeChild(cn);
21571                 node.parentNode.insertBefore(cn, node);
21572                 // move node to parent - and clean it..
21573                 this.replaceTag(cn);
21574             }
21575             node.parentNode.removeChild(node);
21576             /// no need to iterate chidlren = it's got none..
21577             //this.iterateChildren(node, this.cleanWord);
21578             return false; // no need to iterate children.
21579         }
21580         // clean styles
21581         if (node.className.length) {
21582             
21583             var cn = node.className.split(/\W+/);
21584             var cna = [];
21585             Roo.each(cn, function(cls) {
21586                 if (cls.match(/Mso[a-zA-Z]+/)) {
21587                     return;
21588                 }
21589                 cna.push(cls);
21590             });
21591             node.className = cna.length ? cna.join(' ') : '';
21592             if (!cna.length) {
21593                 node.removeAttribute("class");
21594             }
21595         }
21596         
21597         if (node.hasAttribute("lang")) {
21598             node.removeAttribute("lang");
21599         }
21600         
21601         if (node.hasAttribute("style")) {
21602             
21603             var styles = node.getAttribute("style").split(";");
21604             var nstyle = [];
21605             Roo.each(styles, function(s) {
21606                 if (!s.match(/:/)) {
21607                     return;
21608                 }
21609                 var kv = s.split(":");
21610                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21611                     return;
21612                 }
21613                 // what ever is left... we allow.
21614                 nstyle.push(s);
21615             });
21616             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21617             if (!nstyle.length) {
21618                 node.removeAttribute('style');
21619             }
21620         }
21621         return true; // do children
21622         
21623         
21624         
21625     }
21626 });
21627 /**
21628  * @class Roo.htmleditor.FilterStyleToTag
21629  * part of the word stuff... - certain 'styles' should be converted to tags.
21630  * eg.
21631  *   font-weight: bold -> bold
21632  *   ?? super / subscrit etc..
21633  * 
21634  * @constructor
21635 * Run a new style to tag filter.
21636 * @param {Object} config Configuration options
21637  */
21638 Roo.htmleditor.FilterStyleToTag = function(cfg)
21639 {
21640     
21641     this.tags = {
21642         B  : [ 'fontWeight' , 'bold'],
21643         I :  [ 'fontStyle' , 'italic'],
21644         //pre :  [ 'font-style' , 'italic'],
21645         // h1.. h6 ?? font-size?
21646         SUP : [ 'verticalAlign' , 'super' ],
21647         SUB : [ 'verticalAlign' , 'sub' ]
21648         
21649         
21650     };
21651     
21652     Roo.apply(this, cfg);
21653      
21654     
21655     this.walk(cfg.node);
21656     
21657     
21658     
21659 }
21660
21661
21662 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21663 {
21664     tag: true, // all tags
21665     
21666     tags : false,
21667     
21668     
21669     replaceTag : function(node)
21670     {
21671         
21672         
21673         if (node.getAttribute("style") === null) {
21674             return true;
21675         }
21676         var inject = [];
21677         for (var k in this.tags) {
21678             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21679                 inject.push(k);
21680                 node.style.removeProperty(this.tags[k][0]);
21681             }
21682         }
21683         if (!inject.length) {
21684             return true; 
21685         }
21686         var cn = Array.from(node.childNodes);
21687         var nn = node;
21688         Roo.each(inject, function(t) {
21689             var nc = node.ownerDocument.createelement(t);
21690             nn.appendChild(nc);
21691             nn = nc;
21692         });
21693         for(var i = 0;i < cn.length;cn++) {
21694             node.removeChild(cn[i]);
21695             nn.appendChild(cn[i]);
21696         }
21697         return true /// iterate thru
21698     }
21699     
21700 })/**
21701  * @class Roo.htmleditor.FilterLongBr
21702  * BR/BR/BR - keep a maximum of 2...
21703  * @constructor
21704  * Run a new Long BR Filter
21705  * @param {Object} config Configuration options
21706  */
21707
21708 Roo.htmleditor.FilterLongBr = function(cfg)
21709 {
21710     // no need to apply config.
21711     this.walk(cfg.node);
21712 }
21713
21714 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21715 {
21716     
21717      
21718     tag : 'BR',
21719     
21720      
21721     replaceTag : function(node)
21722     {
21723         
21724         var ps = node.nextSibling;
21725         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21726             ps = ps.nextSibling;
21727         }
21728         
21729         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
21730             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21731             return false;
21732         }
21733         
21734         if (!ps || ps.nodeType != 1) {
21735             return false;
21736         }
21737         
21738         if (!ps || ps.tagName != 'BR') {
21739            
21740             return false;
21741         }
21742         
21743         
21744         
21745         
21746         
21747         if (!node.previousSibling) {
21748             return false;
21749         }
21750         var ps = node.previousSibling;
21751         
21752         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21753             ps = ps.previousSibling;
21754         }
21755         if (!ps || ps.nodeType != 1) {
21756             return false;
21757         }
21758         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21759         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21760             return false;
21761         }
21762         
21763         node.parentNode.removeChild(node); // remove me...
21764         
21765         return false; // no need to do children
21766
21767     }
21768     
21769 });
21770 /**
21771  * @class Roo.htmleditor.Tidy
21772  * Tidy HTML 
21773  * @cfg {Roo.HtmlEditorCore} core the editor.
21774  * @constructor
21775  * Create a new Filter.
21776  * @param {Object} config Configuration options
21777  */
21778
21779
21780 Roo.htmleditor.Tidy = function(cfg) {
21781     Roo.apply(this, cfg);
21782     
21783     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
21784      
21785 }
21786
21787 Roo.htmleditor.Tidy.toString = function(node)
21788 {
21789     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
21790 }
21791
21792 Roo.htmleditor.Tidy.prototype = {
21793     
21794     
21795     wrap : function(s) {
21796         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
21797     },
21798
21799     
21800     tidy : function(node, indent) {
21801      
21802         if  (node.nodeType == 3) {
21803             // text.
21804             
21805             
21806             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
21807                 
21808             
21809         }
21810         
21811         if  (node.nodeType != 1) {
21812             return '';
21813         }
21814         
21815         
21816         
21817         if (node.tagName == 'BODY') {
21818             
21819             return this.cn(node, '');
21820         }
21821              
21822              // Prints the node tagName, such as <A>, <IMG>, etc
21823         var ret = "<" + node.tagName +  this.attr(node) ;
21824         
21825         // elements with no children..
21826         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
21827                 return ret + '/>';
21828         }
21829         ret += '>';
21830         
21831         
21832         var cindent = indent === false ? '' : (indent + '  ');
21833         // tags where we will not pad the children.. (inline text tags etc..)
21834         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
21835             cindent = false;
21836             
21837             
21838         }
21839         
21840         var cn = this.cn(node, cindent );
21841         
21842         return ret + cn  + '</' + node.tagName + '>';
21843         
21844     },
21845     cn: function(node, indent)
21846     {
21847         var ret = [];
21848         
21849         var ar = Array.from(node.childNodes);
21850         for (var i = 0 ; i < ar.length ; i++) {
21851             
21852             
21853             
21854             if (indent !== false   // indent==false preservies everything
21855                 && i > 0
21856                 && ar[i].nodeType == 3 
21857                 && ar[i].nodeValue.length > 0
21858                 && ar[i].nodeValue.match(/^\s+/)
21859             ) {
21860                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
21861                     ret.pop(); // remove line break from last?
21862                 }
21863                 
21864                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
21865             }
21866             if (indent !== false
21867                 && ar[i].nodeType == 1 // element - and indent is not set... 
21868             ) {
21869                 ret.push("\n" + indent); 
21870             }
21871             
21872             ret.push(this.tidy(ar[i], indent));
21873             // text + trailing indent 
21874             if (indent !== false
21875                 && ar[i].nodeType == 3
21876                 && ar[i].nodeValue.length > 0
21877                 && ar[i].nodeValue.match(/\s+$/)
21878             ){
21879                 ret.push("\n" + indent); 
21880             }
21881             
21882             
21883             
21884             
21885         }
21886         // what if all text?
21887         
21888         
21889         return ret.join('');
21890     },
21891     
21892          
21893         
21894     attr : function(node)
21895     {
21896         var attr = [];
21897         for(i = 0; i < node.attributes.length;i++) {
21898             
21899             // skip empty values?
21900             if (!node.attributes.item(i).value.length) {
21901                 continue;
21902             }
21903             attr.push(  node.attributes.item(i).name + '="' +
21904                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
21905             );
21906         }
21907         return attr.length ? (' ' + attr.join(' ') ) : '';
21908         
21909     }
21910     
21911     
21912     
21913 }
21914 /**
21915  * @class Roo.htmleditor.KeyEnter
21916  * Handle Enter press..
21917  * @cfg {Roo.HtmlEditorCore} core the editor.
21918  * @constructor
21919  * Create a new Filter.
21920  * @param {Object} config Configuration options
21921  */
21922
21923
21924
21925 Roo.htmleditor.KeyEnter = function(cfg) {
21926     Roo.apply(this, cfg);
21927     // this does not actually call walk as it's really just a abstract class
21928  
21929     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
21930 }
21931
21932
21933 Roo.htmleditor.KeyEnter.prototype = {
21934     
21935     core : false,
21936     
21937     keypress : function(e) {
21938         if (e.charCode != 13) {
21939             return true;
21940         }
21941         e.preventDefault();
21942         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
21943         var doc = this.core.doc;
21944         
21945         var docFragment = doc.createDocumentFragment();
21946     
21947         //add a new line
21948         var newEle = doc.createTextNode('\n');
21949         docFragment.appendChild(newEle);
21950     
21951     
21952         var range = this.core.win.getSelection().getRangeAt(0);
21953         var n = range.commonAncestorContainer ;
21954         while (n && n.nodeType != 1) {
21955             n  = n.parentNode;
21956         }
21957         var li = false;
21958         if (n && n.tagName == 'UL') {
21959             li = doc.createElement('LI');
21960             n.appendChild(li);
21961             
21962         }
21963         if (n && n.tagName == 'LI') {
21964             li = doc.createElement('LI');
21965             if (n.nextSibling) {
21966                 n.parentNode.insertBefore(li, n.firstSibling);
21967                 
21968             } else {
21969                 n.parentNode.appendChild(li);
21970             }
21971         }
21972         if (li) {   
21973             range = doc.createRange();
21974             range.setStartAfter(li);
21975             range.collapse(true);
21976         
21977             //make the cursor there
21978             var sel = this.core.win.getSelection();
21979             sel.removeAllRanges();
21980             sel.addRange(range);
21981             return false;
21982             
21983             
21984         }
21985         //add the br, or p, or something else
21986         newEle = doc.createElement('br');
21987         docFragment.appendChild(newEle);
21988     
21989         //make the br replace selection
21990         
21991         range.deleteContents();
21992         
21993         range.insertNode(docFragment);
21994     
21995         //create a new range
21996         range = doc.createRange();
21997         range.setStartAfter(newEle);
21998         range.collapse(true);
21999     
22000         //make the cursor there
22001         var sel = this.core.win.getSelection();
22002         sel.removeAllRanges();
22003         sel.addRange(range);
22004     
22005         return false;
22006          
22007     }
22008 };
22009      
22010 /**
22011  * @class Roo.htmleditor.Block
22012  * Base class for html editor blocks - do not use it directly .. extend it..
22013  * @cfg {DomElement} node The node to apply stuff to.
22014  * @cfg {String} friendly_name the name that appears in the context bar about this block
22015  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
22016  
22017  * @constructor
22018  * Create a new Filter.
22019  * @param {Object} config Configuration options
22020  */
22021
22022 Roo.htmleditor.Block  = function(cfg)
22023 {
22024     // do nothing .. should not be called really.
22025 }
22026
22027 Roo.htmleditor.Block.factory = function(node)
22028 {
22029     
22030     var id = Roo.get(node).id;
22031     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
22032         Roo.htmleditor.Block.cache[id].readElement();
22033         return Roo.htmleditor.Block.cache[id];
22034     }
22035     
22036     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
22037     if (typeof(cls) == 'undefined') {
22038         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
22039         return false;
22040     }
22041     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
22042     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
22043 };
22044 // question goes here... do we need to clear out this cache sometimes?
22045 // or show we make it relivant to the htmleditor.
22046 Roo.htmleditor.Block.cache = {};
22047
22048 Roo.htmleditor.Block.prototype = {
22049     
22050     node : false,
22051     
22052      // used by context menu
22053     friendly_name : 'Image with caption',
22054     
22055     context : false,
22056     /**
22057      * Update a node with values from this object
22058      * @param {DomElement} node
22059      */
22060     updateElement : function(node)
22061     {
22062         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
22063     },
22064      /**
22065      * convert to plain HTML for calling insertAtCursor..
22066      */
22067     toHTML : function()
22068     {
22069         return Roo.DomHelper.markup(this.toObject());
22070     },
22071     /**
22072      * used by readEleemnt to extract data from a node
22073      * may need improving as it's pretty basic
22074      
22075      * @param {DomElement} node
22076      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
22077      * @param {String} attribute (use html - for contents, or style for using next param as style)
22078      * @param {String} style the style property - eg. text-align
22079      */
22080     getVal : function(node, tag, attr, style)
22081     {
22082         var n = node;
22083         if (tag !== true && n.tagName != tag.toUpperCase()) {
22084             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
22085             // but kiss for now.
22086             n = node.getElementsByTagName(tag).item(0);
22087         }
22088         if (attr == 'html') {
22089             return n.innerHTML;
22090         }
22091         if (attr == 'style') {
22092             return Roo.get(n).getStyle(style);
22093         }
22094         
22095         return Roo.get(n).attr(attr);
22096             
22097     },
22098     /**
22099      * create a DomHelper friendly object - for use with 
22100      * Roo.DomHelper.markup / overwrite / etc..
22101      * (override this)
22102      */
22103     toObject : function()
22104     {
22105         return {};
22106     },
22107       /**
22108      * Read a node that has a 'data-block' property - and extract the values from it.
22109      * @param {DomElement} node - the node
22110      */
22111     readElement : function(node)
22112     {
22113         
22114     } 
22115     
22116     
22117 };
22118
22119  
22120
22121 /**
22122  * @class Roo.htmleditor.BlockFigure
22123  * Block that has an image and a figcaption
22124  * @cfg {String} image_src the url for the image
22125  * @cfg {String} align (left|right) alignment for the block default left
22126  * @cfg {String} text_align (left|right) alignment for the text caption default left.
22127  * @cfg {String} caption the text to appear below  (and in the alt tag)
22128  * @cfg {String|number} image_width the width of the image number or %?
22129  * @cfg {String|number} image_height the height of the image number or %?
22130  * 
22131  * @constructor
22132  * Create a new Filter.
22133  * @param {Object} config Configuration options
22134  */
22135
22136 Roo.htmleditor.BlockFigure = function(cfg)
22137 {
22138     if (cfg.node) {
22139         this.readElement(cfg.node);
22140         this.updateElement(cfg.node);
22141     }
22142     Roo.apply(this, cfg);
22143 }
22144 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
22145  
22146     
22147     // setable values.
22148     image_src: '',
22149     
22150     align: 'left',
22151     caption : '',
22152     text_align: 'left',
22153     
22154     width : '46%',
22155     margin: '2%',
22156     
22157     // used by context menu
22158     friendly_name : 'Image with caption',
22159     
22160     context : { // ?? static really
22161         width : {
22162             title: "Width",
22163             width: 40
22164             // ?? number
22165         },
22166         margin : {
22167             title: "Margin",
22168             width: 40
22169             // ?? number
22170         },
22171         align: {
22172             title: "Align",
22173             opts : [[ "left"],[ "right"]],
22174             width : 80
22175             
22176         },
22177         text_align: {
22178             title: "Caption Align",
22179             opts : [ [ "left"],[ "right"],[ "center"]],
22180             width : 80
22181         },
22182         
22183        
22184         image_src : {
22185             title: "Src",
22186             width: 220
22187         }
22188     },
22189     /**
22190      * create a DomHelper friendly object - for use with
22191      * Roo.DomHelper.markup / overwrite / etc..
22192      */
22193     toObject : function()
22194     {
22195         var d = document.createElement('div');
22196         d.innerHTML = this.caption;
22197         
22198         return {
22199             tag: 'figure',
22200             'data-block' : 'Figure',
22201             contenteditable : 'false',
22202             style : {
22203                 display: 'table',
22204                 float :  this.align ,
22205                 width :  this.width,
22206                 margin:  this.margin
22207             },
22208             cn : [
22209                 {
22210                     tag : 'img',
22211                     src : this.image_src,
22212                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
22213                     style: {
22214                         width: '100%'
22215                     }
22216                 },
22217                 {
22218                     tag: 'figcaption',
22219                     contenteditable : true,
22220                     style : {
22221                         'text-align': this.text_align
22222                     },
22223                     html : this.caption
22224                     
22225                 }
22226             ]
22227         };
22228     },
22229     
22230     readElement : function(node)
22231     {
22232         this.image_src = this.getVal(node, 'img', 'src');
22233         this.align = this.getVal(node, 'figure', 'style', 'float');
22234         this.caption = this.getVal(node, 'figcaption', 'html');
22235         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
22236         this.width = this.getVal(node, 'figure', 'style', 'width');
22237         this.margin = this.getVal(node, 'figure', 'style', 'margin');
22238         
22239     } 
22240     
22241   
22242    
22243      
22244     
22245     
22246     
22247     
22248 })
22249
22250 //<script type="text/javascript">
22251
22252 /*
22253  * Based  Ext JS Library 1.1.1
22254  * Copyright(c) 2006-2007, Ext JS, LLC.
22255  * LGPL
22256  *
22257  */
22258  
22259 /**
22260  * @class Roo.HtmlEditorCore
22261  * @extends Roo.Component
22262  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22263  *
22264  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22265  */
22266
22267 Roo.HtmlEditorCore = function(config){
22268     
22269     
22270     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22271     
22272     
22273     this.addEvents({
22274         /**
22275          * @event initialize
22276          * Fires when the editor is fully initialized (including the iframe)
22277          * @param {Roo.HtmlEditorCore} this
22278          */
22279         initialize: true,
22280         /**
22281          * @event activate
22282          * Fires when the editor is first receives the focus. Any insertion must wait
22283          * until after this event.
22284          * @param {Roo.HtmlEditorCore} this
22285          */
22286         activate: true,
22287          /**
22288          * @event beforesync
22289          * Fires before the textarea is updated with content from the editor iframe. Return false
22290          * to cancel the sync.
22291          * @param {Roo.HtmlEditorCore} this
22292          * @param {String} html
22293          */
22294         beforesync: true,
22295          /**
22296          * @event beforepush
22297          * Fires before the iframe editor is updated with content from the textarea. Return false
22298          * to cancel the push.
22299          * @param {Roo.HtmlEditorCore} this
22300          * @param {String} html
22301          */
22302         beforepush: true,
22303          /**
22304          * @event sync
22305          * Fires when the textarea is updated with content from the editor iframe.
22306          * @param {Roo.HtmlEditorCore} this
22307          * @param {String} html
22308          */
22309         sync: true,
22310          /**
22311          * @event push
22312          * Fires when the iframe editor is updated with content from the textarea.
22313          * @param {Roo.HtmlEditorCore} this
22314          * @param {String} html
22315          */
22316         push: true,
22317         
22318         /**
22319          * @event editorevent
22320          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22321          * @param {Roo.HtmlEditorCore} this
22322          */
22323         editorevent: true
22324         
22325     });
22326     
22327     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22328     
22329     // defaults : white / black...
22330     this.applyBlacklists();
22331     
22332     
22333     
22334 };
22335
22336
22337 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22338
22339
22340      /**
22341      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22342      */
22343     
22344     owner : false,
22345     
22346      /**
22347      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22348      *                        Roo.resizable.
22349      */
22350     resizable : false,
22351      /**
22352      * @cfg {Number} height (in pixels)
22353      */   
22354     height: 300,
22355    /**
22356      * @cfg {Number} width (in pixels)
22357      */   
22358     width: 500,
22359     
22360     /**
22361      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22362      * 
22363      */
22364     stylesheets: false,
22365     
22366     /**
22367      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22368      */
22369     allowComments: false,
22370     // id of frame..
22371     frameId: false,
22372     
22373     // private properties
22374     validationEvent : false,
22375     deferHeight: true,
22376     initialized : false,
22377     activated : false,
22378     sourceEditMode : false,
22379     onFocus : Roo.emptyFn,
22380     iframePad:3,
22381     hideMode:'offsets',
22382     
22383     clearUp: true,
22384     
22385     // blacklist + whitelisted elements..
22386     black: false,
22387     white: false,
22388      
22389     bodyCls : '',
22390
22391     /**
22392      * Protected method that will not generally be called directly. It
22393      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22394      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22395      */
22396     getDocMarkup : function(){
22397         // body styles..
22398         var st = '';
22399         
22400         // inherit styels from page...?? 
22401         if (this.stylesheets === false) {
22402             
22403             Roo.get(document.head).select('style').each(function(node) {
22404                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22405             });
22406             
22407             Roo.get(document.head).select('link').each(function(node) { 
22408                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22409             });
22410             
22411         } else if (!this.stylesheets.length) {
22412                 // simple..
22413                 st = '<style type="text/css">' +
22414                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22415                    '</style>';
22416         } else {
22417             for (var i in this.stylesheets) {
22418                 if (typeof(this.stylesheets[i]) != 'string') {
22419                     continue;
22420                 }
22421                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
22422             }
22423             
22424         }
22425         
22426         st +=  '<style type="text/css">' +
22427             'IMG { cursor: pointer } ' +
22428         '</style>';
22429
22430         var cls = 'roo-htmleditor-body';
22431         
22432         if(this.bodyCls.length){
22433             cls += ' ' + this.bodyCls;
22434         }
22435         
22436         return '<html><head>' + st  +
22437             //<style type="text/css">' +
22438             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22439             //'</style>' +
22440             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22441     },
22442
22443     // private
22444     onRender : function(ct, position)
22445     {
22446         var _t = this;
22447         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22448         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22449         
22450         
22451         this.el.dom.style.border = '0 none';
22452         this.el.dom.setAttribute('tabIndex', -1);
22453         this.el.addClass('x-hidden hide');
22454         
22455         
22456         
22457         if(Roo.isIE){ // fix IE 1px bogus margin
22458             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22459         }
22460        
22461         
22462         this.frameId = Roo.id();
22463         
22464          
22465         
22466         var iframe = this.owner.wrap.createChild({
22467             tag: 'iframe',
22468             cls: 'form-control', // bootstrap..
22469             id: this.frameId,
22470             name: this.frameId,
22471             frameBorder : 'no',
22472             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22473         }, this.el
22474         );
22475         
22476         
22477         this.iframe = iframe.dom;
22478
22479         this.assignDocWin();
22480         
22481         this.doc.designMode = 'on';
22482        
22483         this.doc.open();
22484         this.doc.write(this.getDocMarkup());
22485         this.doc.close();
22486
22487         
22488         var task = { // must defer to wait for browser to be ready
22489             run : function(){
22490                 //console.log("run task?" + this.doc.readyState);
22491                 this.assignDocWin();
22492                 if(this.doc.body || this.doc.readyState == 'complete'){
22493                     try {
22494                         this.doc.designMode="on";
22495                     } catch (e) {
22496                         return;
22497                     }
22498                     Roo.TaskMgr.stop(task);
22499                     this.initEditor.defer(10, this);
22500                 }
22501             },
22502             interval : 10,
22503             duration: 10000,
22504             scope: this
22505         };
22506         Roo.TaskMgr.start(task);
22507
22508     },
22509
22510     // private
22511     onResize : function(w, h)
22512     {
22513          Roo.log('resize: ' +w + ',' + h );
22514         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22515         if(!this.iframe){
22516             return;
22517         }
22518         if(typeof w == 'number'){
22519             
22520             this.iframe.style.width = w + 'px';
22521         }
22522         if(typeof h == 'number'){
22523             
22524             this.iframe.style.height = h + 'px';
22525             if(this.doc){
22526                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22527             }
22528         }
22529         
22530     },
22531
22532     /**
22533      * Toggles the editor between standard and source edit mode.
22534      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22535      */
22536     toggleSourceEdit : function(sourceEditMode){
22537         
22538         this.sourceEditMode = sourceEditMode === true;
22539         
22540         if(this.sourceEditMode){
22541  
22542             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
22543             
22544         }else{
22545             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
22546             //this.iframe.className = '';
22547             this.deferFocus();
22548         }
22549         //this.setSize(this.owner.wrap.getSize());
22550         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22551     },
22552
22553     
22554   
22555
22556     /**
22557      * Protected method that will not generally be called directly. If you need/want
22558      * custom HTML cleanup, this is the method you should override.
22559      * @param {String} html The HTML to be cleaned
22560      * return {String} The cleaned HTML
22561      */
22562     cleanHtml : function(html){
22563         html = String(html);
22564         if(html.length > 5){
22565             if(Roo.isSafari){ // strip safari nonsense
22566                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22567             }
22568         }
22569         if(html == '&nbsp;'){
22570             html = '';
22571         }
22572         return html;
22573     },
22574
22575     /**
22576      * HTML Editor -> Textarea
22577      * Protected method that will not generally be called directly. Syncs the contents
22578      * of the editor iframe with the textarea.
22579      */
22580     syncValue : function()
22581     {
22582         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
22583         if(this.initialized){
22584             var bd = (this.doc.body || this.doc.documentElement);
22585             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22586             
22587             // not sure if this is really the place for this
22588             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
22589             // this has to update attributes that get duped.. like alt and caption..
22590             
22591             
22592             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22593             //     Roo.htmleditor.Block.factory(e);
22594             //},this);
22595             
22596             
22597             var div = document.createElement('div');
22598             div.innerHTML = bd.innerHTML;
22599             // remove content editable. (blocks)
22600             
22601            
22602             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
22603             //?? tidy?
22604             var html = div.innerHTML;
22605             if(Roo.isSafari){
22606                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22607                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22608                 if(m && m[1]){
22609                     html = '<div style="'+m[0]+'">' + html + '</div>';
22610                 }
22611             }
22612             html = this.cleanHtml(html);
22613             // fix up the special chars.. normaly like back quotes in word...
22614             // however we do not want to do this with chinese..
22615             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22616                 
22617                 var cc = match.charCodeAt();
22618
22619                 // Get the character value, handling surrogate pairs
22620                 if (match.length == 2) {
22621                     // It's a surrogate pair, calculate the Unicode code point
22622                     var high = match.charCodeAt(0) - 0xD800;
22623                     var low  = match.charCodeAt(1) - 0xDC00;
22624                     cc = (high * 0x400) + low + 0x10000;
22625                 }  else if (
22626                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22627                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22628                     (cc >= 0xf900 && cc < 0xfb00 )
22629                 ) {
22630                         return match;
22631                 }  
22632          
22633                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22634                 return "&#" + cc + ";";
22635                 
22636                 
22637             });
22638             
22639             
22640              
22641             if(this.owner.fireEvent('beforesync', this, html) !== false){
22642                 this.el.dom.value = html;
22643                 this.owner.fireEvent('sync', this, html);
22644             }
22645         }
22646     },
22647
22648     /**
22649      * TEXTAREA -> EDITABLE
22650      * Protected method that will not generally be called directly. Pushes the value of the textarea
22651      * into the iframe editor.
22652      */
22653     pushValue : function()
22654     {
22655         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
22656         if(this.initialized){
22657             var v = this.el.dom.value.trim();
22658             
22659             
22660             if(this.owner.fireEvent('beforepush', this, v) !== false){
22661                 var d = (this.doc.body || this.doc.documentElement);
22662                 d.innerHTML = v;
22663                  
22664                 this.el.dom.value = d.innerHTML;
22665                 this.owner.fireEvent('push', this, v);
22666             }
22667             
22668             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
22669                 
22670                 Roo.htmleditor.Block.factory(e);
22671                 
22672             },this);
22673             var lc = this.doc.body.lastChild;
22674             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
22675                 // add an extra line at the end.
22676                 this.doc.body.appendChild(this.doc.createElement('br'));
22677             }
22678             
22679             
22680         }
22681     },
22682
22683     // private
22684     deferFocus : function(){
22685         this.focus.defer(10, this);
22686     },
22687
22688     // doc'ed in Field
22689     focus : function(){
22690         if(this.win && !this.sourceEditMode){
22691             this.win.focus();
22692         }else{
22693             this.el.focus();
22694         }
22695     },
22696     
22697     assignDocWin: function()
22698     {
22699         var iframe = this.iframe;
22700         
22701          if(Roo.isIE){
22702             this.doc = iframe.contentWindow.document;
22703             this.win = iframe.contentWindow;
22704         } else {
22705 //            if (!Roo.get(this.frameId)) {
22706 //                return;
22707 //            }
22708 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22709 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22710             
22711             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22712                 return;
22713             }
22714             
22715             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22716             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22717         }
22718     },
22719     
22720     // private
22721     initEditor : function(){
22722         //console.log("INIT EDITOR");
22723         this.assignDocWin();
22724         
22725         
22726         
22727         this.doc.designMode="on";
22728         this.doc.open();
22729         this.doc.write(this.getDocMarkup());
22730         this.doc.close();
22731         
22732         var dbody = (this.doc.body || this.doc.documentElement);
22733         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22734         // this copies styles from the containing element into thsi one..
22735         // not sure why we need all of this..
22736         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22737         
22738         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22739         //ss['background-attachment'] = 'fixed'; // w3c
22740         dbody.bgProperties = 'fixed'; // ie
22741         //Roo.DomHelper.applyStyles(dbody, ss);
22742         Roo.EventManager.on(this.doc, {
22743             //'mousedown': this.onEditorEvent,
22744             'mouseup': this.onEditorEvent,
22745             'dblclick': this.onEditorEvent,
22746             'click': this.onEditorEvent,
22747             'keyup': this.onEditorEvent,
22748             
22749             buffer:100,
22750             scope: this
22751         });
22752         Roo.EventManager.on(this.doc, {
22753             'paste': this.onPasteEvent,
22754             scope : this
22755         });
22756         if(Roo.isGecko){
22757             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22758         }
22759         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22760             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22761         }
22762         this.initialized = true;
22763
22764         
22765         // initialize special key events - enter
22766         new Roo.htmleditor.KeyEnter({core : this});
22767         
22768          
22769         
22770         this.owner.fireEvent('initialize', this);
22771         this.pushValue();
22772     },
22773     
22774     onPasteEvent : function(e,v)
22775     {
22776         // I think we better assume paste is going to be a dirty load of rubish from word..
22777         
22778         // even pasting into a 'email version' of this widget will have to clean up that mess.
22779         var cd = (e.browserEvent.clipboardData || window.clipboardData);
22780         
22781         // check what type of paste - if it's an image, then handle it differently.
22782         if (cd.files.length > 0) {
22783             // pasting images?
22784             var urlAPI = (window.createObjectURL && window) || 
22785                 (window.URL && URL.revokeObjectURL && URL) || 
22786                 (window.webkitURL && webkitURL);
22787     
22788             var url = urlAPI.createObjectURL( cd.files[0]);
22789             this.insertAtCursor('<img src=" + url + ">');
22790             return false;
22791         }
22792         
22793         var html = cd.getData('text/html'); // clipboard event
22794         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
22795         var images = parser.doc.getElementsByType('pict');
22796         Roo.log(images);
22797         //Roo.log(imgs);
22798         // fixme..
22799         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
22800                        .map(function(g) { return g.toDataURL(); });
22801         
22802         
22803         html = this.cleanWordChars(html);
22804         
22805         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
22806         
22807         if (images.length > 0) {
22808             Roo.each(d.getElementsByTagName('img'), function(img, i) {
22809                 img.setAttribute('src', images[i]);
22810             });
22811         }
22812         
22813       
22814         new Roo.htmleditor.FilterStyleToTag({ node : d });
22815         new Roo.htmleditor.FilterAttributes({
22816             node : d,
22817             attrib_white : ['href', 'src', 'name', 'align'],
22818             attrib_clean : ['href', 'src' ] 
22819         });
22820         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
22821         // should be fonts..
22822         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
22823         new Roo.htmleditor.FilterParagraph({ node : d });
22824         new Roo.htmleditor.FilterSpan({ node : d });
22825         new Roo.htmleditor.FilterLongBr({ node : d });
22826         
22827         
22828         
22829         this.insertAtCursor(d.innerHTML);
22830         
22831         e.preventDefault();
22832         return false;
22833         // default behaveiour should be our local cleanup paste? (optional?)
22834         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
22835         //this.owner.fireEvent('paste', e, v);
22836     },
22837     // private
22838     onDestroy : function(){
22839         
22840         
22841         
22842         if(this.rendered){
22843             
22844             //for (var i =0; i < this.toolbars.length;i++) {
22845             //    // fixme - ask toolbars for heights?
22846             //    this.toolbars[i].onDestroy();
22847            // }
22848             
22849             //this.wrap.dom.innerHTML = '';
22850             //this.wrap.remove();
22851         }
22852     },
22853
22854     // private
22855     onFirstFocus : function(){
22856         
22857         this.assignDocWin();
22858         
22859         
22860         this.activated = true;
22861          
22862     
22863         if(Roo.isGecko){ // prevent silly gecko errors
22864             this.win.focus();
22865             var s = this.win.getSelection();
22866             if(!s.focusNode || s.focusNode.nodeType != 3){
22867                 var r = s.getRangeAt(0);
22868                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22869                 r.collapse(true);
22870                 this.deferFocus();
22871             }
22872             try{
22873                 this.execCmd('useCSS', true);
22874                 this.execCmd('styleWithCSS', false);
22875             }catch(e){}
22876         }
22877         this.owner.fireEvent('activate', this);
22878     },
22879
22880     // private
22881     adjustFont: function(btn){
22882         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22883         //if(Roo.isSafari){ // safari
22884         //    adjust *= 2;
22885        // }
22886         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22887         if(Roo.isSafari){ // safari
22888             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22889             v =  (v < 10) ? 10 : v;
22890             v =  (v > 48) ? 48 : v;
22891             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22892             
22893         }
22894         
22895         
22896         v = Math.max(1, v+adjust);
22897         
22898         this.execCmd('FontSize', v  );
22899     },
22900
22901     onEditorEvent : function(e)
22902     {
22903         this.owner.fireEvent('editorevent', this, e);
22904       //  this.updateToolbar();
22905         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22906     },
22907
22908     insertTag : function(tg)
22909     {
22910         // could be a bit smarter... -> wrap the current selected tRoo..
22911         if (tg.toLowerCase() == 'span' ||
22912             tg.toLowerCase() == 'code' ||
22913             tg.toLowerCase() == 'sup' ||
22914             tg.toLowerCase() == 'sub' 
22915             ) {
22916             
22917             range = this.createRange(this.getSelection());
22918             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22919             wrappingNode.appendChild(range.extractContents());
22920             range.insertNode(wrappingNode);
22921
22922             return;
22923             
22924             
22925             
22926         }
22927         this.execCmd("formatblock",   tg);
22928         
22929     },
22930     
22931     insertText : function(txt)
22932     {
22933         
22934         
22935         var range = this.createRange();
22936         range.deleteContents();
22937                //alert(Sender.getAttribute('label'));
22938                
22939         range.insertNode(this.doc.createTextNode(txt));
22940     } ,
22941     
22942      
22943
22944     /**
22945      * Executes a Midas editor command on the editor document and performs necessary focus and
22946      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22947      * @param {String} cmd The Midas command
22948      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22949      */
22950     relayCmd : function(cmd, value){
22951         this.win.focus();
22952         this.execCmd(cmd, value);
22953         this.owner.fireEvent('editorevent', this);
22954         //this.updateToolbar();
22955         this.owner.deferFocus();
22956     },
22957
22958     /**
22959      * Executes a Midas editor command directly on the editor document.
22960      * For visual commands, you should use {@link #relayCmd} instead.
22961      * <b>This should only be called after the editor is initialized.</b>
22962      * @param {String} cmd The Midas command
22963      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22964      */
22965     execCmd : function(cmd, value){
22966         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22967         this.syncValue();
22968     },
22969  
22970  
22971    
22972     /**
22973      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22974      * to insert tRoo.
22975      * @param {String} text | dom node.. 
22976      */
22977     insertAtCursor : function(text)
22978     {
22979         
22980         if(!this.activated){
22981             return;
22982         }
22983          
22984         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22985             this.win.focus();
22986             
22987             
22988             // from jquery ui (MIT licenced)
22989             var range, node;
22990             var win = this.win;
22991             
22992             if (win.getSelection && win.getSelection().getRangeAt) {
22993                 
22994                 // delete the existing?
22995                 
22996                 this.createRange(this.getSelection()).deleteContents();
22997                 range = win.getSelection().getRangeAt(0);
22998                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22999                 range.insertNode(node);
23000             } else if (win.document.selection && win.document.selection.createRange) {
23001                 // no firefox support
23002                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23003                 win.document.selection.createRange().pasteHTML(txt);
23004             } else {
23005                 // no firefox support
23006                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23007                 this.execCmd('InsertHTML', txt);
23008             } 
23009             
23010             this.syncValue();
23011             
23012             this.deferFocus();
23013         }
23014     },
23015  // private
23016     mozKeyPress : function(e){
23017         if(e.ctrlKey){
23018             var c = e.getCharCode(), cmd;
23019           
23020             if(c > 0){
23021                 c = String.fromCharCode(c).toLowerCase();
23022                 switch(c){
23023                     case 'b':
23024                         cmd = 'bold';
23025                         break;
23026                     case 'i':
23027                         cmd = 'italic';
23028                         break;
23029                     
23030                     case 'u':
23031                         cmd = 'underline';
23032                         break;
23033                     
23034                     //case 'v':
23035                       //  this.cleanUpPaste.defer(100, this);
23036                       //  return;
23037                         
23038                 }
23039                 if(cmd){
23040                     this.win.focus();
23041                     this.execCmd(cmd);
23042                     this.deferFocus();
23043                     e.preventDefault();
23044                 }
23045                 
23046             }
23047         }
23048     },
23049
23050     // private
23051     fixKeys : function(){ // load time branching for fastest keydown performance
23052         if(Roo.isIE){
23053             return function(e){
23054                 var k = e.getKey(), r;
23055                 if(k == e.TAB){
23056                     e.stopEvent();
23057                     r = this.doc.selection.createRange();
23058                     if(r){
23059                         r.collapse(true);
23060                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23061                         this.deferFocus();
23062                     }
23063                     return;
23064                 }
23065                 
23066                 if(k == e.ENTER){
23067                     r = this.doc.selection.createRange();
23068                     if(r){
23069                         var target = r.parentElement();
23070                         if(!target || target.tagName.toLowerCase() != 'li'){
23071                             e.stopEvent();
23072                             r.pasteHTML('<br/>');
23073                             r.collapse(false);
23074                             r.select();
23075                         }
23076                     }
23077                 }
23078                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23079                 //    this.cleanUpPaste.defer(100, this);
23080                 //    return;
23081                 //}
23082                 
23083                 
23084             };
23085         }else if(Roo.isOpera){
23086             return function(e){
23087                 var k = e.getKey();
23088                 if(k == e.TAB){
23089                     e.stopEvent();
23090                     this.win.focus();
23091                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23092                     this.deferFocus();
23093                 }
23094                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23095                 //    this.cleanUpPaste.defer(100, this);
23096                  //   return;
23097                 //}
23098                 
23099             };
23100         }else if(Roo.isSafari){
23101             return function(e){
23102                 var k = e.getKey();
23103                 
23104                 if(k == e.TAB){
23105                     e.stopEvent();
23106                     this.execCmd('InsertText','\t');
23107                     this.deferFocus();
23108                     return;
23109                 }
23110                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23111                  //   this.cleanUpPaste.defer(100, this);
23112                  //   return;
23113                // }
23114                 
23115              };
23116         }
23117     }(),
23118     
23119     getAllAncestors: function()
23120     {
23121         var p = this.getSelectedNode();
23122         var a = [];
23123         if (!p) {
23124             a.push(p); // push blank onto stack..
23125             p = this.getParentElement();
23126         }
23127         
23128         
23129         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23130             a.push(p);
23131             p = p.parentNode;
23132         }
23133         a.push(this.doc.body);
23134         return a;
23135     },
23136     lastSel : false,
23137     lastSelNode : false,
23138     
23139     
23140     getSelection : function() 
23141     {
23142         this.assignDocWin();
23143         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23144     },
23145     /**
23146      * Select a dom node
23147      * @param {DomElement} node the node to select
23148      */
23149     selectNode : function(node)
23150     {
23151         
23152             var nodeRange = node.ownerDocument.createRange();
23153             try {
23154                 nodeRange.selectNode(node);
23155             } catch (e) {
23156                 nodeRange.selectNodeContents(node);
23157             }
23158             //nodeRange.collapse(true);
23159             var s = this.win.getSelection();
23160             s.removeAllRanges();
23161             s.addRange(nodeRange);
23162     },
23163     
23164     getSelectedNode: function() 
23165     {
23166         // this may only work on Gecko!!!
23167         
23168         // should we cache this!!!!
23169         
23170         
23171         
23172          
23173         var range = this.createRange(this.getSelection()).cloneRange();
23174         
23175         if (Roo.isIE) {
23176             var parent = range.parentElement();
23177             while (true) {
23178                 var testRange = range.duplicate();
23179                 testRange.moveToElementText(parent);
23180                 if (testRange.inRange(range)) {
23181                     break;
23182                 }
23183                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23184                     break;
23185                 }
23186                 parent = parent.parentElement;
23187             }
23188             return parent;
23189         }
23190         
23191         // is ancestor a text element.
23192         var ac =  range.commonAncestorContainer;
23193         if (ac.nodeType == 3) {
23194             ac = ac.parentNode;
23195         }
23196         
23197         var ar = ac.childNodes;
23198          
23199         var nodes = [];
23200         var other_nodes = [];
23201         var has_other_nodes = false;
23202         for (var i=0;i<ar.length;i++) {
23203             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23204                 continue;
23205             }
23206             // fullly contained node.
23207             
23208             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23209                 nodes.push(ar[i]);
23210                 continue;
23211             }
23212             
23213             // probably selected..
23214             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23215                 other_nodes.push(ar[i]);
23216                 continue;
23217             }
23218             // outer..
23219             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23220                 continue;
23221             }
23222             
23223             
23224             has_other_nodes = true;
23225         }
23226         if (!nodes.length && other_nodes.length) {
23227             nodes= other_nodes;
23228         }
23229         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23230             return false;
23231         }
23232         
23233         return nodes[0];
23234     },
23235     createRange: function(sel)
23236     {
23237         // this has strange effects when using with 
23238         // top toolbar - not sure if it's a great idea.
23239         //this.editor.contentWindow.focus();
23240         if (typeof sel != "undefined") {
23241             try {
23242                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23243             } catch(e) {
23244                 return this.doc.createRange();
23245             }
23246         } else {
23247             return this.doc.createRange();
23248         }
23249     },
23250     getParentElement: function()
23251     {
23252         
23253         this.assignDocWin();
23254         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23255         
23256         var range = this.createRange(sel);
23257          
23258         try {
23259             var p = range.commonAncestorContainer;
23260             while (p.nodeType == 3) { // text node
23261                 p = p.parentNode;
23262             }
23263             return p;
23264         } catch (e) {
23265             return null;
23266         }
23267     
23268     },
23269     /***
23270      *
23271      * Range intersection.. the hard stuff...
23272      *  '-1' = before
23273      *  '0' = hits..
23274      *  '1' = after.
23275      *         [ -- selected range --- ]
23276      *   [fail]                        [fail]
23277      *
23278      *    basically..
23279      *      if end is before start or  hits it. fail.
23280      *      if start is after end or hits it fail.
23281      *
23282      *   if either hits (but other is outside. - then it's not 
23283      *   
23284      *    
23285      **/
23286     
23287     
23288     // @see http://www.thismuchiknow.co.uk/?p=64.
23289     rangeIntersectsNode : function(range, node)
23290     {
23291         var nodeRange = node.ownerDocument.createRange();
23292         try {
23293             nodeRange.selectNode(node);
23294         } catch (e) {
23295             nodeRange.selectNodeContents(node);
23296         }
23297     
23298         var rangeStartRange = range.cloneRange();
23299         rangeStartRange.collapse(true);
23300     
23301         var rangeEndRange = range.cloneRange();
23302         rangeEndRange.collapse(false);
23303     
23304         var nodeStartRange = nodeRange.cloneRange();
23305         nodeStartRange.collapse(true);
23306     
23307         var nodeEndRange = nodeRange.cloneRange();
23308         nodeEndRange.collapse(false);
23309     
23310         return rangeStartRange.compareBoundaryPoints(
23311                  Range.START_TO_START, nodeEndRange) == -1 &&
23312                rangeEndRange.compareBoundaryPoints(
23313                  Range.START_TO_START, nodeStartRange) == 1;
23314         
23315          
23316     },
23317     rangeCompareNode : function(range, node)
23318     {
23319         var nodeRange = node.ownerDocument.createRange();
23320         try {
23321             nodeRange.selectNode(node);
23322         } catch (e) {
23323             nodeRange.selectNodeContents(node);
23324         }
23325         
23326         
23327         range.collapse(true);
23328     
23329         nodeRange.collapse(true);
23330      
23331         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23332         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23333          
23334         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23335         
23336         var nodeIsBefore   =  ss == 1;
23337         var nodeIsAfter    = ee == -1;
23338         
23339         if (nodeIsBefore && nodeIsAfter) {
23340             return 0; // outer
23341         }
23342         if (!nodeIsBefore && nodeIsAfter) {
23343             return 1; //right trailed.
23344         }
23345         
23346         if (nodeIsBefore && !nodeIsAfter) {
23347             return 2;  // left trailed.
23348         }
23349         // fully contined.
23350         return 3;
23351     },
23352  
23353     cleanWordChars : function(input) {// change the chars to hex code
23354         
23355        var swapCodes  = [ 
23356             [    8211, "&#8211;" ], 
23357             [    8212, "&#8212;" ], 
23358             [    8216,  "'" ],  
23359             [    8217, "'" ],  
23360             [    8220, '"' ],  
23361             [    8221, '"' ],  
23362             [    8226, "*" ],  
23363             [    8230, "..." ]
23364         ]; 
23365         var output = input;
23366         Roo.each(swapCodes, function(sw) { 
23367             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23368             
23369             output = output.replace(swapper, sw[1]);
23370         });
23371         
23372         return output;
23373     },
23374     
23375      
23376     
23377         
23378     
23379     cleanUpChild : function (node)
23380     {
23381         
23382         new Roo.htmleditor.FilterComment({node : node});
23383         new Roo.htmleditor.FilterAttributes({
23384                 node : node,
23385                 attrib_black : this.ablack,
23386                 attrib_clean : this.aclean,
23387                 style_white : this.cwhite,
23388                 style_black : this.cblack
23389         });
23390         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
23391         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
23392          
23393         
23394     },
23395     
23396     /**
23397      * Clean up MS wordisms...
23398      * @deprecated - use filter directly
23399      */
23400     cleanWord : function(node)
23401     {
23402         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
23403         
23404     },
23405    
23406     
23407     /**
23408
23409      * @deprecated - use filters
23410      */
23411     cleanTableWidths : function(node)
23412     {
23413         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
23414         
23415  
23416     },
23417     
23418      
23419         
23420     applyBlacklists : function()
23421     {
23422         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23423         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23424         
23425         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
23426         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
23427         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
23428         
23429         this.white = [];
23430         this.black = [];
23431         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23432             if (b.indexOf(tag) > -1) {
23433                 return;
23434             }
23435             this.white.push(tag);
23436             
23437         }, this);
23438         
23439         Roo.each(w, function(tag) {
23440             if (b.indexOf(tag) > -1) {
23441                 return;
23442             }
23443             if (this.white.indexOf(tag) > -1) {
23444                 return;
23445             }
23446             this.white.push(tag);
23447             
23448         }, this);
23449         
23450         
23451         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23452             if (w.indexOf(tag) > -1) {
23453                 return;
23454             }
23455             this.black.push(tag);
23456             
23457         }, this);
23458         
23459         Roo.each(b, function(tag) {
23460             if (w.indexOf(tag) > -1) {
23461                 return;
23462             }
23463             if (this.black.indexOf(tag) > -1) {
23464                 return;
23465             }
23466             this.black.push(tag);
23467             
23468         }, this);
23469         
23470         
23471         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23472         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23473         
23474         this.cwhite = [];
23475         this.cblack = [];
23476         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23477             if (b.indexOf(tag) > -1) {
23478                 return;
23479             }
23480             this.cwhite.push(tag);
23481             
23482         }, this);
23483         
23484         Roo.each(w, function(tag) {
23485             if (b.indexOf(tag) > -1) {
23486                 return;
23487             }
23488             if (this.cwhite.indexOf(tag) > -1) {
23489                 return;
23490             }
23491             this.cwhite.push(tag);
23492             
23493         }, this);
23494         
23495         
23496         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23497             if (w.indexOf(tag) > -1) {
23498                 return;
23499             }
23500             this.cblack.push(tag);
23501             
23502         }, this);
23503         
23504         Roo.each(b, function(tag) {
23505             if (w.indexOf(tag) > -1) {
23506                 return;
23507             }
23508             if (this.cblack.indexOf(tag) > -1) {
23509                 return;
23510             }
23511             this.cblack.push(tag);
23512             
23513         }, this);
23514     },
23515     
23516     setStylesheets : function(stylesheets)
23517     {
23518         if(typeof(stylesheets) == 'string'){
23519             Roo.get(this.iframe.contentDocument.head).createChild({
23520                 tag : 'link',
23521                 rel : 'stylesheet',
23522                 type : 'text/css',
23523                 href : stylesheets
23524             });
23525             
23526             return;
23527         }
23528         var _this = this;
23529      
23530         Roo.each(stylesheets, function(s) {
23531             if(!s.length){
23532                 return;
23533             }
23534             
23535             Roo.get(_this.iframe.contentDocument.head).createChild({
23536                 tag : 'link',
23537                 rel : 'stylesheet',
23538                 type : 'text/css',
23539                 href : s
23540             });
23541         });
23542
23543         
23544     },
23545     
23546     removeStylesheets : function()
23547     {
23548         var _this = this;
23549         
23550         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23551             s.remove();
23552         });
23553     },
23554     
23555     setStyle : function(style)
23556     {
23557         Roo.get(this.iframe.contentDocument.head).createChild({
23558             tag : 'style',
23559             type : 'text/css',
23560             html : style
23561         });
23562
23563         return;
23564     }
23565     
23566     // hide stuff that is not compatible
23567     /**
23568      * @event blur
23569      * @hide
23570      */
23571     /**
23572      * @event change
23573      * @hide
23574      */
23575     /**
23576      * @event focus
23577      * @hide
23578      */
23579     /**
23580      * @event specialkey
23581      * @hide
23582      */
23583     /**
23584      * @cfg {String} fieldClass @hide
23585      */
23586     /**
23587      * @cfg {String} focusClass @hide
23588      */
23589     /**
23590      * @cfg {String} autoCreate @hide
23591      */
23592     /**
23593      * @cfg {String} inputType @hide
23594      */
23595     /**
23596      * @cfg {String} invalidClass @hide
23597      */
23598     /**
23599      * @cfg {String} invalidText @hide
23600      */
23601     /**
23602      * @cfg {String} msgFx @hide
23603      */
23604     /**
23605      * @cfg {String} validateOnBlur @hide
23606      */
23607 });
23608
23609 Roo.HtmlEditorCore.white = [
23610         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
23611         
23612        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
23613        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
23614        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
23615        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
23616        'TABLE',   'UL',         'XMP', 
23617        
23618        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
23619       'THEAD',   'TR', 
23620      
23621       'DIR', 'MENU', 'OL', 'UL', 'DL',
23622        
23623       'EMBED',  'OBJECT'
23624 ];
23625
23626
23627 Roo.HtmlEditorCore.black = [
23628     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23629         'APPLET', // 
23630         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
23631         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
23632         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
23633         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
23634         //'FONT' // CLEAN LATER..
23635         'COLGROUP', 'COL'  // messy tables.
23636         
23637 ];
23638 Roo.HtmlEditorCore.clean = [ // ?? needed???
23639      'SCRIPT', 'STYLE', 'TITLE', 'XML'
23640 ];
23641 Roo.HtmlEditorCore.tag_remove = [
23642     'FONT', 'TBODY'  
23643 ];
23644 // attributes..
23645
23646 Roo.HtmlEditorCore.ablack = [
23647     'on'
23648 ];
23649     
23650 Roo.HtmlEditorCore.aclean = [ 
23651     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23652 ];
23653
23654 // protocols..
23655 Roo.HtmlEditorCore.pwhite= [
23656         'http',  'https',  'mailto'
23657 ];
23658
23659 // white listed style attributes.
23660 Roo.HtmlEditorCore.cwhite= [
23661       //  'text-align', /// default is to allow most things..
23662       
23663          
23664 //        'font-size'//??
23665 ];
23666
23667 // black listed style attributes.
23668 Roo.HtmlEditorCore.cblack= [
23669       //  'font-size' -- this can be set by the project 
23670 ];
23671
23672
23673
23674
23675     //<script type="text/javascript">
23676
23677 /*
23678  * Ext JS Library 1.1.1
23679  * Copyright(c) 2006-2007, Ext JS, LLC.
23680  * Licence LGPL
23681  * 
23682  */
23683  
23684  
23685 Roo.form.HtmlEditor = function(config){
23686     
23687     
23688     
23689     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
23690     
23691     if (!this.toolbars) {
23692         this.toolbars = [];
23693     }
23694     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23695     
23696     
23697 };
23698
23699 /**
23700  * @class Roo.form.HtmlEditor
23701  * @extends Roo.form.Field
23702  * Provides a lightweight HTML Editor component.
23703  *
23704  * This has been tested on Fireforx / Chrome.. IE may not be so great..
23705  * 
23706  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23707  * supported by this editor.</b><br/><br/>
23708  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23709  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23710  */
23711 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
23712     /**
23713      * @cfg {Boolean} clearUp
23714      */
23715     clearUp : true,
23716       /**
23717      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23718      */
23719     toolbars : false,
23720    
23721      /**
23722      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23723      *                        Roo.resizable.
23724      */
23725     resizable : false,
23726      /**
23727      * @cfg {Number} height (in pixels)
23728      */   
23729     height: 300,
23730    /**
23731      * @cfg {Number} width (in pixels)
23732      */   
23733     width: 500,
23734     
23735     /**
23736      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
23737      * 
23738      */
23739     stylesheets: false,
23740     
23741     
23742      /**
23743      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
23744      * 
23745      */
23746     cblack: false,
23747     /**
23748      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
23749      * 
23750      */
23751     cwhite: false,
23752     
23753      /**
23754      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
23755      * 
23756      */
23757     black: false,
23758     /**
23759      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
23760      * 
23761      */
23762     white: false,
23763     /**
23764      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
23765      */
23766     allowComments: false,
23767     /**
23768      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
23769      */
23770     
23771     
23772      bodyCls : '',
23773     
23774     // id of frame..
23775     frameId: false,
23776     
23777     // private properties
23778     validationEvent : false,
23779     deferHeight: true,
23780     initialized : false,
23781     activated : false,
23782     
23783     onFocus : Roo.emptyFn,
23784     iframePad:3,
23785     hideMode:'offsets',
23786     
23787     actionMode : 'container', // defaults to hiding it...
23788     
23789     defaultAutoCreate : { // modified by initCompnoent..
23790         tag: "textarea",
23791         style:"width:500px;height:300px;",
23792         autocomplete: "new-password"
23793     },
23794
23795     // private
23796     initComponent : function(){
23797         this.addEvents({
23798             /**
23799              * @event initialize
23800              * Fires when the editor is fully initialized (including the iframe)
23801              * @param {HtmlEditor} this
23802              */
23803             initialize: true,
23804             /**
23805              * @event activate
23806              * Fires when the editor is first receives the focus. Any insertion must wait
23807              * until after this event.
23808              * @param {HtmlEditor} this
23809              */
23810             activate: true,
23811              /**
23812              * @event beforesync
23813              * Fires before the textarea is updated with content from the editor iframe. Return false
23814              * to cancel the sync.
23815              * @param {HtmlEditor} this
23816              * @param {String} html
23817              */
23818             beforesync: true,
23819              /**
23820              * @event beforepush
23821              * Fires before the iframe editor is updated with content from the textarea. Return false
23822              * to cancel the push.
23823              * @param {HtmlEditor} this
23824              * @param {String} html
23825              */
23826             beforepush: true,
23827              /**
23828              * @event sync
23829              * Fires when the textarea is updated with content from the editor iframe.
23830              * @param {HtmlEditor} this
23831              * @param {String} html
23832              */
23833             sync: true,
23834              /**
23835              * @event push
23836              * Fires when the iframe editor is updated with content from the textarea.
23837              * @param {HtmlEditor} this
23838              * @param {String} html
23839              */
23840             push: true,
23841              /**
23842              * @event editmodechange
23843              * Fires when the editor switches edit modes
23844              * @param {HtmlEditor} this
23845              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23846              */
23847             editmodechange: true,
23848             /**
23849              * @event editorevent
23850              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23851              * @param {HtmlEditor} this
23852              */
23853             editorevent: true,
23854             /**
23855              * @event firstfocus
23856              * Fires when on first focus - needed by toolbars..
23857              * @param {HtmlEditor} this
23858              */
23859             firstfocus: true,
23860             /**
23861              * @event autosave
23862              * Auto save the htmlEditor value as a file into Events
23863              * @param {HtmlEditor} this
23864              */
23865             autosave: true,
23866             /**
23867              * @event savedpreview
23868              * preview the saved version of htmlEditor
23869              * @param {HtmlEditor} this
23870              */
23871             savedpreview: true,
23872             
23873             /**
23874             * @event stylesheetsclick
23875             * Fires when press the Sytlesheets button
23876             * @param {Roo.HtmlEditorCore} this
23877             */
23878             stylesheetsclick: true,
23879             /**
23880             * @event paste
23881             * Fires when press user pastes into the editor
23882             * @param {Roo.HtmlEditorCore} this
23883             */
23884             paste: true 
23885         });
23886         this.defaultAutoCreate =  {
23887             tag: "textarea",
23888             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
23889             autocomplete: "new-password"
23890         };
23891     },
23892
23893     /**
23894      * Protected method that will not generally be called directly. It
23895      * is called when the editor creates its toolbar. Override this method if you need to
23896      * add custom toolbar buttons.
23897      * @param {HtmlEditor} editor
23898      */
23899     createToolbar : function(editor){
23900         Roo.log("create toolbars");
23901         if (!editor.toolbars || !editor.toolbars.length) {
23902             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23903         }
23904         
23905         for (var i =0 ; i < editor.toolbars.length;i++) {
23906             editor.toolbars[i] = Roo.factory(
23907                     typeof(editor.toolbars[i]) == 'string' ?
23908                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
23909                 Roo.form.HtmlEditor);
23910             editor.toolbars[i].init(editor);
23911         }
23912          
23913         
23914     },
23915
23916      
23917     // private
23918     onRender : function(ct, position)
23919     {
23920         var _t = this;
23921         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23922         
23923         this.wrap = this.el.wrap({
23924             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23925         });
23926         
23927         this.editorcore.onRender(ct, position);
23928          
23929         if (this.resizable) {
23930             this.resizeEl = new Roo.Resizable(this.wrap, {
23931                 pinned : true,
23932                 wrap: true,
23933                 dynamic : true,
23934                 minHeight : this.height,
23935                 height: this.height,
23936                 handles : this.resizable,
23937                 width: this.width,
23938                 listeners : {
23939                     resize : function(r, w, h) {
23940                         _t.onResize(w,h); // -something
23941                     }
23942                 }
23943             });
23944             
23945         }
23946         this.createToolbar(this);
23947        
23948         
23949         if(!this.width){
23950             this.setSize(this.wrap.getSize());
23951         }
23952         if (this.resizeEl) {
23953             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23954             // should trigger onReize..
23955         }
23956         
23957         this.keyNav = new Roo.KeyNav(this.el, {
23958             
23959             "tab" : function(e){
23960                 e.preventDefault();
23961                 
23962                 var value = this.getValue();
23963                 
23964                 var start = this.el.dom.selectionStart;
23965                 var end = this.el.dom.selectionEnd;
23966                 
23967                 if(!e.shiftKey){
23968                     
23969                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
23970                     this.el.dom.setSelectionRange(end + 1, end + 1);
23971                     return;
23972                 }
23973                 
23974                 var f = value.substring(0, start).split("\t");
23975                 
23976                 if(f.pop().length != 0){
23977                     return;
23978                 }
23979                 
23980                 this.setValue(f.join("\t") + value.substring(end));
23981                 this.el.dom.setSelectionRange(start - 1, start - 1);
23982                 
23983             },
23984             
23985             "home" : function(e){
23986                 e.preventDefault();
23987                 
23988                 var curr = this.el.dom.selectionStart;
23989                 var lines = this.getValue().split("\n");
23990                 
23991                 if(!lines.length){
23992                     return;
23993                 }
23994                 
23995                 if(e.ctrlKey){
23996                     this.el.dom.setSelectionRange(0, 0);
23997                     return;
23998                 }
23999                 
24000                 var pos = 0;
24001                 
24002                 for (var i = 0; i < lines.length;i++) {
24003                     pos += lines[i].length;
24004                     
24005                     if(i != 0){
24006                         pos += 1;
24007                     }
24008                     
24009                     if(pos < curr){
24010                         continue;
24011                     }
24012                     
24013                     pos -= lines[i].length;
24014                     
24015                     break;
24016                 }
24017                 
24018                 if(!e.shiftKey){
24019                     this.el.dom.setSelectionRange(pos, pos);
24020                     return;
24021                 }
24022                 
24023                 this.el.dom.selectionStart = pos;
24024                 this.el.dom.selectionEnd = curr;
24025             },
24026             
24027             "end" : function(e){
24028                 e.preventDefault();
24029                 
24030                 var curr = this.el.dom.selectionStart;
24031                 var lines = this.getValue().split("\n");
24032                 
24033                 if(!lines.length){
24034                     return;
24035                 }
24036                 
24037                 if(e.ctrlKey){
24038                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
24039                     return;
24040                 }
24041                 
24042                 var pos = 0;
24043                 
24044                 for (var i = 0; i < lines.length;i++) {
24045                     
24046                     pos += lines[i].length;
24047                     
24048                     if(i != 0){
24049                         pos += 1;
24050                     }
24051                     
24052                     if(pos < curr){
24053                         continue;
24054                     }
24055                     
24056                     break;
24057                 }
24058                 
24059                 if(!e.shiftKey){
24060                     this.el.dom.setSelectionRange(pos, pos);
24061                     return;
24062                 }
24063                 
24064                 this.el.dom.selectionStart = curr;
24065                 this.el.dom.selectionEnd = pos;
24066             },
24067
24068             scope : this,
24069
24070             doRelay : function(foo, bar, hname){
24071                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
24072             },
24073
24074             forceKeyDown: true
24075         });
24076         
24077 //        if(this.autosave && this.w){
24078 //            this.autoSaveFn = setInterval(this.autosave, 1000);
24079 //        }
24080     },
24081
24082     // private
24083     onResize : function(w, h)
24084     {
24085         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24086         var ew = false;
24087         var eh = false;
24088         
24089         if(this.el ){
24090             if(typeof w == 'number'){
24091                 var aw = w - this.wrap.getFrameWidth('lr');
24092                 this.el.setWidth(this.adjustWidth('textarea', aw));
24093                 ew = aw;
24094             }
24095             if(typeof h == 'number'){
24096                 var tbh = 0;
24097                 for (var i =0; i < this.toolbars.length;i++) {
24098                     // fixme - ask toolbars for heights?
24099                     tbh += this.toolbars[i].tb.el.getHeight();
24100                     if (this.toolbars[i].footer) {
24101                         tbh += this.toolbars[i].footer.el.getHeight();
24102                     }
24103                 }
24104                 
24105                 
24106                 
24107                 
24108                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24109                 ah -= 5; // knock a few pixes off for look..
24110 //                Roo.log(ah);
24111                 this.el.setHeight(this.adjustWidth('textarea', ah));
24112                 var eh = ah;
24113             }
24114         }
24115         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24116         this.editorcore.onResize(ew,eh);
24117         
24118     },
24119
24120     /**
24121      * Toggles the editor between standard and source edit mode.
24122      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24123      */
24124     toggleSourceEdit : function(sourceEditMode)
24125     {
24126         this.editorcore.toggleSourceEdit(sourceEditMode);
24127         
24128         if(this.editorcore.sourceEditMode){
24129             Roo.log('editor - showing textarea');
24130             
24131 //            Roo.log('in');
24132 //            Roo.log(this.syncValue());
24133             this.editorcore.syncValue();
24134             this.el.removeClass('x-hidden');
24135             this.el.dom.removeAttribute('tabIndex');
24136             this.el.focus();
24137             this.el.dom.scrollTop = 0;
24138             
24139             
24140             for (var i = 0; i < this.toolbars.length; i++) {
24141                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24142                     this.toolbars[i].tb.hide();
24143                     this.toolbars[i].footer.hide();
24144                 }
24145             }
24146             
24147         }else{
24148             Roo.log('editor - hiding textarea');
24149 //            Roo.log('out')
24150 //            Roo.log(this.pushValue()); 
24151             this.editorcore.pushValue();
24152             
24153             this.el.addClass('x-hidden');
24154             this.el.dom.setAttribute('tabIndex', -1);
24155             
24156             for (var i = 0; i < this.toolbars.length; i++) {
24157                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
24158                     this.toolbars[i].tb.show();
24159                     this.toolbars[i].footer.show();
24160                 }
24161             }
24162             
24163             //this.deferFocus();
24164         }
24165         
24166         this.setSize(this.wrap.getSize());
24167         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
24168         
24169         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24170     },
24171  
24172     // private (for BoxComponent)
24173     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24174
24175     // private (for BoxComponent)
24176     getResizeEl : function(){
24177         return this.wrap;
24178     },
24179
24180     // private (for BoxComponent)
24181     getPositionEl : function(){
24182         return this.wrap;
24183     },
24184
24185     // private
24186     initEvents : function(){
24187         this.originalValue = this.getValue();
24188     },
24189
24190     /**
24191      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24192      * @method
24193      */
24194     markInvalid : Roo.emptyFn,
24195     /**
24196      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24197      * @method
24198      */
24199     clearInvalid : Roo.emptyFn,
24200
24201     setValue : function(v){
24202         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24203         this.editorcore.pushValue();
24204     },
24205
24206      
24207     // private
24208     deferFocus : function(){
24209         this.focus.defer(10, this);
24210     },
24211
24212     // doc'ed in Field
24213     focus : function(){
24214         this.editorcore.focus();
24215         
24216     },
24217       
24218
24219     // private
24220     onDestroy : function(){
24221         
24222         
24223         
24224         if(this.rendered){
24225             
24226             for (var i =0; i < this.toolbars.length;i++) {
24227                 // fixme - ask toolbars for heights?
24228                 this.toolbars[i].onDestroy();
24229             }
24230             
24231             this.wrap.dom.innerHTML = '';
24232             this.wrap.remove();
24233         }
24234     },
24235
24236     // private
24237     onFirstFocus : function(){
24238         //Roo.log("onFirstFocus");
24239         this.editorcore.onFirstFocus();
24240          for (var i =0; i < this.toolbars.length;i++) {
24241             this.toolbars[i].onFirstFocus();
24242         }
24243         
24244     },
24245     
24246     // private
24247     syncValue : function()
24248     {
24249         this.editorcore.syncValue();
24250     },
24251     
24252     pushValue : function()
24253     {
24254         this.editorcore.pushValue();
24255     },
24256     
24257     setStylesheets : function(stylesheets)
24258     {
24259         this.editorcore.setStylesheets(stylesheets);
24260     },
24261     
24262     removeStylesheets : function()
24263     {
24264         this.editorcore.removeStylesheets();
24265     }
24266      
24267     
24268     // hide stuff that is not compatible
24269     /**
24270      * @event blur
24271      * @hide
24272      */
24273     /**
24274      * @event change
24275      * @hide
24276      */
24277     /**
24278      * @event focus
24279      * @hide
24280      */
24281     /**
24282      * @event specialkey
24283      * @hide
24284      */
24285     /**
24286      * @cfg {String} fieldClass @hide
24287      */
24288     /**
24289      * @cfg {String} focusClass @hide
24290      */
24291     /**
24292      * @cfg {String} autoCreate @hide
24293      */
24294     /**
24295      * @cfg {String} inputType @hide
24296      */
24297     /**
24298      * @cfg {String} invalidClass @hide
24299      */
24300     /**
24301      * @cfg {String} invalidText @hide
24302      */
24303     /**
24304      * @cfg {String} msgFx @hide
24305      */
24306     /**
24307      * @cfg {String} validateOnBlur @hide
24308      */
24309 });
24310  
24311     // <script type="text/javascript">
24312 /*
24313  * Based on
24314  * Ext JS Library 1.1.1
24315  * Copyright(c) 2006-2007, Ext JS, LLC.
24316  *  
24317  
24318  */
24319
24320 /**
24321  * @class Roo.form.HtmlEditorToolbar1
24322  * Basic Toolbar
24323  * 
24324  * Usage:
24325  *
24326  new Roo.form.HtmlEditor({
24327     ....
24328     toolbars : [
24329         new Roo.form.HtmlEditorToolbar1({
24330             disable : { fonts: 1 , format: 1, ..., ... , ...],
24331             btns : [ .... ]
24332         })
24333     }
24334      
24335  * 
24336  * @cfg {Object} disable List of elements to disable..
24337  * @cfg {Array} btns List of additional buttons.
24338  * 
24339  * 
24340  * NEEDS Extra CSS? 
24341  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24342  */
24343  
24344 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24345 {
24346     
24347     Roo.apply(this, config);
24348     
24349     // default disabled, based on 'good practice'..
24350     this.disable = this.disable || {};
24351     Roo.applyIf(this.disable, {
24352         fontSize : true,
24353         colors : true,
24354         specialElements : true
24355     });
24356     
24357     
24358     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24359     // dont call parent... till later.
24360 }
24361
24362 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24363     
24364     tb: false,
24365     
24366     rendered: false,
24367     
24368     editor : false,
24369     editorcore : false,
24370     /**
24371      * @cfg {Object} disable  List of toolbar elements to disable
24372          
24373      */
24374     disable : false,
24375     
24376     
24377      /**
24378      * @cfg {String} createLinkText The default text for the create link prompt
24379      */
24380     createLinkText : 'Please enter the URL for the link:',
24381     /**
24382      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24383      */
24384     defaultLinkValue : 'http:/'+'/',
24385    
24386     
24387       /**
24388      * @cfg {Array} fontFamilies An array of available font families
24389      */
24390     fontFamilies : [
24391         'Arial',
24392         'Courier New',
24393         'Tahoma',
24394         'Times New Roman',
24395         'Verdana'
24396     ],
24397     
24398     specialChars : [
24399            "&#169;",
24400           "&#174;",     
24401           "&#8482;",    
24402           "&#163;" ,    
24403          // "&#8212;",    
24404           "&#8230;",    
24405           "&#247;" ,    
24406         //  "&#225;" ,     ?? a acute?
24407            "&#8364;"    , //Euro
24408        //   "&#8220;"    ,
24409         //  "&#8221;"    ,
24410         //  "&#8226;"    ,
24411           "&#176;"  //   , // degrees
24412
24413          // "&#233;"     , // e ecute
24414          // "&#250;"     , // u ecute?
24415     ],
24416     
24417     specialElements : [
24418         {
24419             text: "Insert Table",
24420             xtype: 'MenuItem',
24421             xns : Roo.Menu,
24422             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
24423                 
24424         },
24425         {    
24426             text: "Insert Image",
24427             xtype: 'MenuItem',
24428             xns : Roo.Menu,
24429             ihtml : '<img src="about:blank"/>'
24430             
24431         }
24432         
24433          
24434     ],
24435     
24436     
24437     inputElements : [ 
24438             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24439             "input:submit", "input:button", "select", "textarea", "label" ],
24440     formats : [
24441         ["p"] ,  
24442         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24443         ["pre"],[ "code"], 
24444         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
24445         ['div'],['span'],
24446         ['sup'],['sub']
24447     ],
24448     
24449     cleanStyles : [
24450         "font-size"
24451     ],
24452      /**
24453      * @cfg {String} defaultFont default font to use.
24454      */
24455     defaultFont: 'tahoma',
24456    
24457     fontSelect : false,
24458     
24459     
24460     formatCombo : false,
24461     
24462     init : function(editor)
24463     {
24464         this.editor = editor;
24465         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24466         var editorcore = this.editorcore;
24467         
24468         var _t = this;
24469         
24470         var fid = editorcore.frameId;
24471         var etb = this;
24472         function btn(id, toggle, handler){
24473             var xid = fid + '-'+ id ;
24474             return {
24475                 id : xid,
24476                 cmd : id,
24477                 cls : 'x-btn-icon x-edit-'+id,
24478                 enableToggle:toggle !== false,
24479                 scope: _t, // was editor...
24480                 handler:handler||_t.relayBtnCmd,
24481                 clickEvent:'mousedown',
24482                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24483                 tabIndex:-1
24484             };
24485         }
24486         
24487         
24488         
24489         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24490         this.tb = tb;
24491          // stop form submits
24492         tb.el.on('click', function(e){
24493             e.preventDefault(); // what does this do?
24494         });
24495
24496         if(!this.disable.font) { // && !Roo.isSafari){
24497             /* why no safari for fonts 
24498             editor.fontSelect = tb.el.createChild({
24499                 tag:'select',
24500                 tabIndex: -1,
24501                 cls:'x-font-select',
24502                 html: this.createFontOptions()
24503             });
24504             
24505             editor.fontSelect.on('change', function(){
24506                 var font = editor.fontSelect.dom.value;
24507                 editor.relayCmd('fontname', font);
24508                 editor.deferFocus();
24509             }, editor);
24510             
24511             tb.add(
24512                 editor.fontSelect.dom,
24513                 '-'
24514             );
24515             */
24516             
24517         };
24518         if(!this.disable.formats){
24519             this.formatCombo = new Roo.form.ComboBox({
24520                 store: new Roo.data.SimpleStore({
24521                     id : 'tag',
24522                     fields: ['tag'],
24523                     data : this.formats // from states.js
24524                 }),
24525                 blockFocus : true,
24526                 name : '',
24527                 //autoCreate : {tag: "div",  size: "20"},
24528                 displayField:'tag',
24529                 typeAhead: false,
24530                 mode: 'local',
24531                 editable : false,
24532                 triggerAction: 'all',
24533                 emptyText:'Add tag',
24534                 selectOnFocus:true,
24535                 width:135,
24536                 listeners : {
24537                     'select': function(c, r, i) {
24538                         editorcore.insertTag(r.get('tag'));
24539                         editor.focus();
24540                     }
24541                 }
24542
24543             });
24544             tb.addField(this.formatCombo);
24545             
24546         }
24547         
24548         if(!this.disable.format){
24549             tb.add(
24550                 btn('bold'),
24551                 btn('italic'),
24552                 btn('underline'),
24553                 btn('strikethrough')
24554             );
24555         };
24556         if(!this.disable.fontSize){
24557             tb.add(
24558                 '-',
24559                 
24560                 
24561                 btn('increasefontsize', false, editorcore.adjustFont),
24562                 btn('decreasefontsize', false, editorcore.adjustFont)
24563             );
24564         };
24565         
24566         
24567         if(!this.disable.colors){
24568             tb.add(
24569                 '-', {
24570                     id:editorcore.frameId +'-forecolor',
24571                     cls:'x-btn-icon x-edit-forecolor',
24572                     clickEvent:'mousedown',
24573                     tooltip: this.buttonTips['forecolor'] || undefined,
24574                     tabIndex:-1,
24575                     menu : new Roo.menu.ColorMenu({
24576                         allowReselect: true,
24577                         focus: Roo.emptyFn,
24578                         value:'000000',
24579                         plain:true,
24580                         selectHandler: function(cp, color){
24581                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24582                             editor.deferFocus();
24583                         },
24584                         scope: editorcore,
24585                         clickEvent:'mousedown'
24586                     })
24587                 }, {
24588                     id:editorcore.frameId +'backcolor',
24589                     cls:'x-btn-icon x-edit-backcolor',
24590                     clickEvent:'mousedown',
24591                     tooltip: this.buttonTips['backcolor'] || undefined,
24592                     tabIndex:-1,
24593                     menu : new Roo.menu.ColorMenu({
24594                         focus: Roo.emptyFn,
24595                         value:'FFFFFF',
24596                         plain:true,
24597                         allowReselect: true,
24598                         selectHandler: function(cp, color){
24599                             if(Roo.isGecko){
24600                                 editorcore.execCmd('useCSS', false);
24601                                 editorcore.execCmd('hilitecolor', color);
24602                                 editorcore.execCmd('useCSS', true);
24603                                 editor.deferFocus();
24604                             }else{
24605                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24606                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24607                                 editor.deferFocus();
24608                             }
24609                         },
24610                         scope:editorcore,
24611                         clickEvent:'mousedown'
24612                     })
24613                 }
24614             );
24615         };
24616         // now add all the items...
24617         
24618
24619         if(!this.disable.alignments){
24620             tb.add(
24621                 '-',
24622                 btn('justifyleft'),
24623                 btn('justifycenter'),
24624                 btn('justifyright')
24625             );
24626         };
24627
24628         //if(!Roo.isSafari){
24629             if(!this.disable.links){
24630                 tb.add(
24631                     '-',
24632                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
24633                 );
24634             };
24635
24636             if(!this.disable.lists){
24637                 tb.add(
24638                     '-',
24639                     btn('insertorderedlist'),
24640                     btn('insertunorderedlist')
24641                 );
24642             }
24643             if(!this.disable.sourceEdit){
24644                 tb.add(
24645                     '-',
24646                     btn('sourceedit', true, function(btn){
24647                         this.toggleSourceEdit(btn.pressed);
24648                     })
24649                 );
24650             }
24651         //}
24652         
24653         var smenu = { };
24654         // special menu.. - needs to be tidied up..
24655         if (!this.disable.special) {
24656             smenu = {
24657                 text: "&#169;",
24658                 cls: 'x-edit-none',
24659                 
24660                 menu : {
24661                     items : []
24662                 }
24663             };
24664             for (var i =0; i < this.specialChars.length; i++) {
24665                 smenu.menu.items.push({
24666                     
24667                     html: this.specialChars[i],
24668                     handler: function(a,b) {
24669                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
24670                         //editor.insertAtCursor(a.html);
24671                         
24672                     },
24673                     tabIndex:-1
24674                 });
24675             }
24676             
24677             
24678             tb.add(smenu);
24679             
24680             
24681         }
24682         
24683         var cmenu = { };
24684         if (!this.disable.cleanStyles) {
24685             cmenu = {
24686                 cls: 'x-btn-icon x-btn-clear',
24687                 
24688                 menu : {
24689                     items : []
24690                 }
24691             };
24692             for (var i =0; i < this.cleanStyles.length; i++) {
24693                 cmenu.menu.items.push({
24694                     actiontype : this.cleanStyles[i],
24695                     html: 'Remove ' + this.cleanStyles[i],
24696                     handler: function(a,b) {
24697 //                        Roo.log(a);
24698 //                        Roo.log(b);
24699                         var c = Roo.get(editorcore.doc.body);
24700                         c.select('[style]').each(function(s) {
24701                             s.dom.style.removeProperty(a.actiontype);
24702                         });
24703                         editorcore.syncValue();
24704                     },
24705                     tabIndex:-1
24706                 });
24707             }
24708             cmenu.menu.items.push({
24709                 actiontype : 'tablewidths',
24710                 html: 'Remove Table Widths',
24711                 handler: function(a,b) {
24712                     editorcore.cleanTableWidths();
24713                     editorcore.syncValue();
24714                 },
24715                 tabIndex:-1
24716             });
24717             cmenu.menu.items.push({
24718                 actiontype : 'word',
24719                 html: 'Remove MS Word Formating',
24720                 handler: function(a,b) {
24721                     editorcore.cleanWord();
24722                     editorcore.syncValue();
24723                 },
24724                 tabIndex:-1
24725             });
24726             
24727             cmenu.menu.items.push({
24728                 actiontype : 'all',
24729                 html: 'Remove All Styles',
24730                 handler: function(a,b) {
24731                     
24732                     var c = Roo.get(editorcore.doc.body);
24733                     c.select('[style]').each(function(s) {
24734                         s.dom.removeAttribute('style');
24735                     });
24736                     editorcore.syncValue();
24737                 },
24738                 tabIndex:-1
24739             });
24740             
24741             cmenu.menu.items.push({
24742                 actiontype : 'all',
24743                 html: 'Remove All CSS Classes',
24744                 handler: function(a,b) {
24745                     
24746                     var c = Roo.get(editorcore.doc.body);
24747                     c.select('[class]').each(function(s) {
24748                         s.dom.removeAttribute('class');
24749                     });
24750                     editorcore.cleanWord();
24751                     editorcore.syncValue();
24752                 },
24753                 tabIndex:-1
24754             });
24755             
24756              cmenu.menu.items.push({
24757                 actiontype : 'tidy',
24758                 html: 'Tidy HTML Source',
24759                 handler: function(a,b) {
24760                     new Roo.htmleditor.Tidy(editorcore.doc.body);
24761                     editorcore.syncValue();
24762                 },
24763                 tabIndex:-1
24764             });
24765             
24766             
24767             tb.add(cmenu);
24768         }
24769          
24770         if (!this.disable.specialElements) {
24771             var semenu = {
24772                 text: "Other;",
24773                 cls: 'x-edit-none',
24774                 menu : {
24775                     items : []
24776                 }
24777             };
24778             for (var i =0; i < this.specialElements.length; i++) {
24779                 semenu.menu.items.push(
24780                     Roo.apply({ 
24781                         handler: function(a,b) {
24782                             editor.insertAtCursor(this.ihtml);
24783                         }
24784                     }, this.specialElements[i])
24785                 );
24786                     
24787             }
24788             
24789             tb.add(semenu);
24790             
24791             
24792         }
24793          
24794         
24795         if (this.btns) {
24796             for(var i =0; i< this.btns.length;i++) {
24797                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
24798                 b.cls =  'x-edit-none';
24799                 
24800                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
24801                     b.cls += ' x-init-enable';
24802                 }
24803                 
24804                 b.scope = editorcore;
24805                 tb.add(b);
24806             }
24807         
24808         }
24809         
24810         
24811         
24812         // disable everything...
24813         
24814         this.tb.items.each(function(item){
24815             
24816            if(
24817                 item.id != editorcore.frameId+ '-sourceedit' && 
24818                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
24819             ){
24820                 
24821                 item.disable();
24822             }
24823         });
24824         this.rendered = true;
24825         
24826         // the all the btns;
24827         editor.on('editorevent', this.updateToolbar, this);
24828         // other toolbars need to implement this..
24829         //editor.on('editmodechange', this.updateToolbar, this);
24830     },
24831     
24832     
24833     relayBtnCmd : function(btn) {
24834         this.editorcore.relayCmd(btn.cmd);
24835     },
24836     // private used internally
24837     createLink : function(){
24838         Roo.log("create link?");
24839         var url = prompt(this.createLinkText, this.defaultLinkValue);
24840         if(url && url != 'http:/'+'/'){
24841             this.editorcore.relayCmd('createlink', url);
24842         }
24843     },
24844
24845     
24846     /**
24847      * Protected method that will not generally be called directly. It triggers
24848      * a toolbar update by reading the markup state of the current selection in the editor.
24849      */
24850     updateToolbar: function(){
24851
24852         if(!this.editorcore.activated){
24853             this.editor.onFirstFocus();
24854             return;
24855         }
24856
24857         var btns = this.tb.items.map, 
24858             doc = this.editorcore.doc,
24859             frameId = this.editorcore.frameId;
24860
24861         if(!this.disable.font && !Roo.isSafari){
24862             /*
24863             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24864             if(name != this.fontSelect.dom.value){
24865                 this.fontSelect.dom.value = name;
24866             }
24867             */
24868         }
24869         if(!this.disable.format){
24870             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24871             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24872             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24873             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
24874         }
24875         if(!this.disable.alignments){
24876             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24877             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24878             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24879         }
24880         if(!Roo.isSafari && !this.disable.lists){
24881             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24882             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24883         }
24884         
24885         var ans = this.editorcore.getAllAncestors();
24886         if (this.formatCombo) {
24887             
24888             
24889             var store = this.formatCombo.store;
24890             this.formatCombo.setValue("");
24891             for (var i =0; i < ans.length;i++) {
24892                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24893                     // select it..
24894                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24895                     break;
24896                 }
24897             }
24898         }
24899         
24900         
24901         
24902         // hides menus... - so this cant be on a menu...
24903         Roo.menu.MenuMgr.hideAll();
24904
24905         //this.editorsyncValue();
24906     },
24907    
24908     
24909     createFontOptions : function(){
24910         var buf = [], fs = this.fontFamilies, ff, lc;
24911         
24912         
24913         
24914         for(var i = 0, len = fs.length; i< len; i++){
24915             ff = fs[i];
24916             lc = ff.toLowerCase();
24917             buf.push(
24918                 '<option value="',lc,'" style="font-family:',ff,';"',
24919                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24920                     ff,
24921                 '</option>'
24922             );
24923         }
24924         return buf.join('');
24925     },
24926     
24927     toggleSourceEdit : function(sourceEditMode){
24928         
24929         Roo.log("toolbar toogle");
24930         if(sourceEditMode === undefined){
24931             sourceEditMode = !this.sourceEditMode;
24932         }
24933         this.sourceEditMode = sourceEditMode === true;
24934         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
24935         // just toggle the button?
24936         if(btn.pressed !== this.sourceEditMode){
24937             btn.toggle(this.sourceEditMode);
24938             return;
24939         }
24940         
24941         if(sourceEditMode){
24942             Roo.log("disabling buttons");
24943             this.tb.items.each(function(item){
24944                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
24945                     item.disable();
24946                 }
24947             });
24948           
24949         }else{
24950             Roo.log("enabling buttons");
24951             if(this.editorcore.initialized){
24952                 this.tb.items.each(function(item){
24953                     item.enable();
24954                 });
24955             }
24956             
24957         }
24958         Roo.log("calling toggole on editor");
24959         // tell the editor that it's been pressed..
24960         this.editor.toggleSourceEdit(sourceEditMode);
24961        
24962     },
24963      /**
24964      * Object collection of toolbar tooltips for the buttons in the editor. The key
24965      * is the command id associated with that button and the value is a valid QuickTips object.
24966      * For example:
24967 <pre><code>
24968 {
24969     bold : {
24970         title: 'Bold (Ctrl+B)',
24971         text: 'Make the selected text bold.',
24972         cls: 'x-html-editor-tip'
24973     },
24974     italic : {
24975         title: 'Italic (Ctrl+I)',
24976         text: 'Make the selected text italic.',
24977         cls: 'x-html-editor-tip'
24978     },
24979     ...
24980 </code></pre>
24981     * @type Object
24982      */
24983     buttonTips : {
24984         bold : {
24985             title: 'Bold (Ctrl+B)',
24986             text: 'Make the selected text bold.',
24987             cls: 'x-html-editor-tip'
24988         },
24989         italic : {
24990             title: 'Italic (Ctrl+I)',
24991             text: 'Make the selected text italic.',
24992             cls: 'x-html-editor-tip'
24993         },
24994         underline : {
24995             title: 'Underline (Ctrl+U)',
24996             text: 'Underline the selected text.',
24997             cls: 'x-html-editor-tip'
24998         },
24999         strikethrough : {
25000             title: 'Strikethrough',
25001             text: 'Strikethrough the selected text.',
25002             cls: 'x-html-editor-tip'
25003         },
25004         increasefontsize : {
25005             title: 'Grow Text',
25006             text: 'Increase the font size.',
25007             cls: 'x-html-editor-tip'
25008         },
25009         decreasefontsize : {
25010             title: 'Shrink Text',
25011             text: 'Decrease the font size.',
25012             cls: 'x-html-editor-tip'
25013         },
25014         backcolor : {
25015             title: 'Text Highlight Color',
25016             text: 'Change the background color of the selected text.',
25017             cls: 'x-html-editor-tip'
25018         },
25019         forecolor : {
25020             title: 'Font Color',
25021             text: 'Change the color of the selected text.',
25022             cls: 'x-html-editor-tip'
25023         },
25024         justifyleft : {
25025             title: 'Align Text Left',
25026             text: 'Align text to the left.',
25027             cls: 'x-html-editor-tip'
25028         },
25029         justifycenter : {
25030             title: 'Center Text',
25031             text: 'Center text in the editor.',
25032             cls: 'x-html-editor-tip'
25033         },
25034         justifyright : {
25035             title: 'Align Text Right',
25036             text: 'Align text to the right.',
25037             cls: 'x-html-editor-tip'
25038         },
25039         insertunorderedlist : {
25040             title: 'Bullet List',
25041             text: 'Start a bulleted list.',
25042             cls: 'x-html-editor-tip'
25043         },
25044         insertorderedlist : {
25045             title: 'Numbered List',
25046             text: 'Start a numbered list.',
25047             cls: 'x-html-editor-tip'
25048         },
25049         createlink : {
25050             title: 'Hyperlink',
25051             text: 'Make the selected text a hyperlink.',
25052             cls: 'x-html-editor-tip'
25053         },
25054         sourceedit : {
25055             title: 'Source Edit',
25056             text: 'Switch to source editing mode.',
25057             cls: 'x-html-editor-tip'
25058         }
25059     },
25060     // private
25061     onDestroy : function(){
25062         if(this.rendered){
25063             
25064             this.tb.items.each(function(item){
25065                 if(item.menu){
25066                     item.menu.removeAll();
25067                     if(item.menu.el){
25068                         item.menu.el.destroy();
25069                     }
25070                 }
25071                 item.destroy();
25072             });
25073              
25074         }
25075     },
25076     onFirstFocus: function() {
25077         this.tb.items.each(function(item){
25078            item.enable();
25079         });
25080     }
25081 });
25082
25083
25084
25085
25086 // <script type="text/javascript">
25087 /*
25088  * Based on
25089  * Ext JS Library 1.1.1
25090  * Copyright(c) 2006-2007, Ext JS, LLC.
25091  *  
25092  
25093  */
25094
25095  
25096 /**
25097  * @class Roo.form.HtmlEditor.ToolbarContext
25098  * Context Toolbar
25099  * 
25100  * Usage:
25101  *
25102  new Roo.form.HtmlEditor({
25103     ....
25104     toolbars : [
25105         { xtype: 'ToolbarStandard', styles : {} }
25106         { xtype: 'ToolbarContext', disable : {} }
25107     ]
25108 })
25109
25110      
25111  * 
25112  * @config : {Object} disable List of elements to disable.. (not done yet.)
25113  * @config : {Object} styles  Map of styles available.
25114  * 
25115  */
25116
25117 Roo.form.HtmlEditor.ToolbarContext = function(config)
25118 {
25119     
25120     Roo.apply(this, config);
25121     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25122     // dont call parent... till later.
25123     this.styles = this.styles || {};
25124 }
25125
25126  
25127
25128 Roo.form.HtmlEditor.ToolbarContext.types = {
25129     'IMG' : {
25130         width : {
25131             title: "Width",
25132             width: 40
25133         },
25134         height:  {
25135             title: "Height",
25136             width: 40
25137         },
25138         align: {
25139             title: "Align",
25140             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25141             width : 80
25142             
25143         },
25144         border: {
25145             title: "Border",
25146             width: 40
25147         },
25148         alt: {
25149             title: "Alt",
25150             width: 120
25151         },
25152         src : {
25153             title: "Src",
25154             width: 220
25155         }
25156         
25157     },
25158     
25159     'FIGURE' : {
25160          align: {
25161             title: "Align",
25162             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25163             width : 80  
25164         }
25165     },
25166     'A' : {
25167         name : {
25168             title: "Name",
25169             width: 50
25170         },
25171         target:  {
25172             title: "Target",
25173             width: 120
25174         },
25175         href:  {
25176             title: "Href",
25177             width: 220
25178         } // border?
25179         
25180     },
25181     /*
25182     'TABLE' : {
25183         rows : {
25184             title: "Rows",
25185             width: 20
25186         },
25187         cols : {
25188             title: "Cols",
25189             width: 20
25190         },
25191         width : {
25192             title: "Width",
25193             width: 40
25194         },
25195         height : {
25196             title: "Height",
25197             width: 40
25198         },
25199         border : {
25200             title: "Border",
25201             width: 20
25202         }
25203     },
25204     'TD' : {
25205         width : {
25206             title: "Width",
25207             width: 40
25208         },
25209         height : {
25210             title: "Height",
25211             width: 40
25212         },   
25213         align: {
25214             title: "Align",
25215             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25216             width: 80
25217         },
25218         valign: {
25219             title: "Valign",
25220             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25221             width: 80
25222         },
25223         colspan: {
25224             title: "Colspan",
25225             width: 20
25226             
25227         },
25228          'font-family'  : {
25229             title : "Font",
25230             style : 'fontFamily',
25231             displayField: 'display',
25232             optname : 'font-family',
25233             width: 140
25234         }
25235     },
25236     */
25237     'INPUT' : {
25238         name : {
25239             title: "name",
25240             width: 120
25241         },
25242         value : {
25243             title: "Value",
25244             width: 120
25245         },
25246         width : {
25247             title: "Width",
25248             width: 40
25249         }
25250     },
25251     'LABEL' : {
25252         'for' : {
25253             title: "For",
25254             width: 120
25255         }
25256     },
25257     'TEXTAREA' : {
25258         name : {
25259             title: "name",
25260             width: 120
25261         },
25262         rows : {
25263             title: "Rows",
25264             width: 20
25265         },
25266         cols : {
25267             title: "Cols",
25268             width: 20
25269         }
25270     },
25271     'SELECT' : {
25272         name : {
25273             title: "name",
25274             width: 120
25275         },
25276         selectoptions : {
25277             title: "Options",
25278             width: 200
25279         }
25280     },
25281     
25282     // should we really allow this??
25283     // should this just be 
25284     'BODY' : {
25285         title : {
25286             title: "Title",
25287             width: 200,
25288             disabled : true
25289         }
25290     },
25291     /*
25292     'SPAN' : {
25293         'font-family'  : {
25294             title : "Font",
25295             style : 'fontFamily',
25296             displayField: 'display',
25297             optname : 'font-family',
25298             width: 140
25299         }
25300     },
25301     'DIV' : {
25302         'font-family'  : {
25303             title : "Font",
25304             style : 'fontFamily',
25305             displayField: 'display',
25306             optname : 'font-family',
25307             width: 140
25308         }
25309     },
25310      'P' : {
25311         'font-family'  : {
25312             title : "Font",
25313             style : 'fontFamily',
25314             displayField: 'display',
25315             optname : 'font-family',
25316             width: 140
25317         }
25318     },
25319     */
25320     '*' : {
25321         // empty..
25322     }
25323
25324 };
25325
25326 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
25327 Roo.form.HtmlEditor.ToolbarContext.stores = false;
25328
25329 Roo.form.HtmlEditor.ToolbarContext.options = {
25330         'font-family'  : [ 
25331                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
25332                 [ 'Courier New', 'Courier New'],
25333                 [ 'Tahoma', 'Tahoma'],
25334                 [ 'Times New Roman,serif', 'Times'],
25335                 [ 'Verdana','Verdana' ]
25336         ]
25337 };
25338
25339 // fixme - these need to be configurable..
25340  
25341
25342 //Roo.form.HtmlEditor.ToolbarContext.types
25343
25344
25345 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25346     
25347     tb: false,
25348     
25349     rendered: false,
25350     
25351     editor : false,
25352     editorcore : false,
25353     /**
25354      * @cfg {Object} disable  List of toolbar elements to disable
25355          
25356      */
25357     disable : false,
25358     /**
25359      * @cfg {Object} styles List of styles 
25360      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
25361      *
25362      * These must be defined in the page, so they get rendered correctly..
25363      * .headline { }
25364      * TD.underline { }
25365      * 
25366      */
25367     styles : false,
25368     
25369     options: false,
25370     
25371     toolbars : false,
25372     
25373     init : function(editor)
25374     {
25375         this.editor = editor;
25376         this.editorcore = editor.editorcore ? editor.editorcore : editor;
25377         var editorcore = this.editorcore;
25378         
25379         var fid = editorcore.frameId;
25380         var etb = this;
25381         function btn(id, toggle, handler){
25382             var xid = fid + '-'+ id ;
25383             return {
25384                 id : xid,
25385                 cmd : id,
25386                 cls : 'x-btn-icon x-edit-'+id,
25387                 enableToggle:toggle !== false,
25388                 scope: editorcore, // was editor...
25389                 handler:handler||editorcore.relayBtnCmd,
25390                 clickEvent:'mousedown',
25391                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25392                 tabIndex:-1
25393             };
25394         }
25395         // create a new element.
25396         var wdiv = editor.wrap.createChild({
25397                 tag: 'div'
25398             }, editor.wrap.dom.firstChild.nextSibling, true);
25399         
25400         // can we do this more than once??
25401         
25402          // stop form submits
25403       
25404  
25405         // disable everything...
25406         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25407         this.toolbars = {};
25408            
25409         for (var i in  ty) {
25410           
25411             this.toolbars[i] = this.buildToolbar(ty[i],i);
25412         }
25413         this.tb = this.toolbars.BODY;
25414         this.tb.el.show();
25415         this.buildFooter();
25416         this.footer.show();
25417         editor.on('hide', function( ) { this.footer.hide() }, this);
25418         editor.on('show', function( ) { this.footer.show() }, this);
25419         
25420          
25421         this.rendered = true;
25422         
25423         // the all the btns;
25424         editor.on('editorevent', this.updateToolbar, this);
25425         // other toolbars need to implement this..
25426         //editor.on('editmodechange', this.updateToolbar, this);
25427     },
25428     
25429     
25430     
25431     /**
25432      * Protected method that will not generally be called directly. It triggers
25433      * a toolbar update by reading the markup state of the current selection in the editor.
25434      *
25435      * Note you can force an update by calling on('editorevent', scope, false)
25436      */
25437     updateToolbar: function(editor ,ev, sel)
25438     {
25439         
25440         if (ev) {
25441             ev.stopEvent(); // se if we can stop this looping with mutiple events.
25442         }
25443         
25444         //Roo.log(ev);
25445         // capture mouse up - this is handy for selecting images..
25446         // perhaps should go somewhere else...
25447         if(!this.editorcore.activated){
25448              this.editor.onFirstFocus();
25449             return;
25450         }
25451         Roo.log(ev ? ev.target : 'NOTARGET');
25452         
25453         
25454         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
25455         // selectNode - might want to handle IE?
25456         
25457         
25458         
25459         if (ev &&
25460             (ev.type == 'mouseup' || ev.type == 'click' ) &&
25461             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
25462             // they have click on an image...
25463             // let's see if we can change the selection...
25464             sel = ev.target;
25465             
25466             // this triggers looping?
25467             //this.editorcore.selectNode(sel);
25468              
25469         }  
25470         
25471       
25472         //var updateFooter = sel ? false : true; 
25473         
25474         
25475         var ans = this.editorcore.getAllAncestors();
25476         
25477         // pick
25478         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
25479         
25480         if (!sel) { 
25481             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
25482             sel = sel ? sel : this.editorcore.doc.body;
25483             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
25484             
25485         }
25486         
25487         var tn = sel.tagName.toUpperCase();
25488         var lastSel = this.tb.selectedNode;
25489         this.tb.selectedNode = sel;
25490         var left_label = tn;
25491         
25492         // ok see if we are editing a block?
25493         var sel_el = Roo.get(sel);
25494         var db = false;
25495         // you are not actually selecting the block.
25496         if (sel && sel.hasAttribute('data-block')) {
25497             db = sel;
25498         } else if (sel && !sel.hasAttribute('contenteditable')) {
25499             db = sel_el.findParent('[data-block]');
25500             var cepar = sel_el.findParent('[contenteditable=true]');
25501             if (db && cepar && cepar.tagName != 'BODY') {
25502                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
25503             }   
25504         }
25505         
25506         
25507         var block = false;
25508         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
25509         if (db) {
25510             block = Roo.htmleditor.Block.factory(db);
25511             if (block) {
25512                 tn = 'BLOCK.' + db.getAttribute('data-block');
25513                 
25514                 //this.editorcore.selectNode(db);
25515                 if (typeof(this.toolbars[tn]) == 'undefined') {
25516                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
25517                 }
25518                 this.toolbars[tn].selectedNode = db;
25519                 left_label = block.friendly_name;
25520                 ans = this.editorcore.getAllAncestors();
25521             }
25522             
25523                 
25524             
25525         }
25526         
25527         
25528         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
25529             return; // no change?
25530         }
25531         
25532         
25533           
25534         this.tb.el.hide();
25535         ///console.log("show: " + tn);
25536         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
25537         
25538         this.tb.el.show();
25539         // update name
25540         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
25541         
25542         
25543         // update attributes
25544         if (block) {
25545              
25546             this.tb.fields.each(function(e) {
25547                 e.setValue(block[e.name]);
25548             });
25549             
25550             
25551         } else  if (this.tb.fields && this.tb.selectedNode) {
25552             this.tb.fields.each( function(e) {
25553                 if (e.stylename) {
25554                     e.setValue(this.tb.selectedNode.style[e.stylename]);
25555                     return;
25556                 } 
25557                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
25558             }, this);
25559             this.updateToolbarStyles(this.tb.selectedNode);  
25560         }
25561         
25562         
25563        
25564         Roo.menu.MenuMgr.hideAll();
25565
25566         
25567         
25568     
25569         // update the footer
25570         //
25571         this.updateFooter(ans);
25572              
25573     },
25574     
25575     updateToolbarStyles : function(sel)
25576     {
25577         var hasStyles = false;
25578         for(var i in this.styles) {
25579             hasStyles = true;
25580             break;
25581         }
25582         
25583         // update styles
25584         if (hasStyles && this.tb.hasStyles) { 
25585             var st = this.tb.fields.item(0);
25586             
25587             st.store.removeAll();
25588             var cn = sel.className.split(/\s+/);
25589             
25590             var avs = [];
25591             if (this.styles['*']) {
25592                 
25593                 Roo.each(this.styles['*'], function(v) {
25594                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25595                 });
25596             }
25597             if (this.styles[tn]) { 
25598                 Roo.each(this.styles[tn], function(v) {
25599                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
25600                 });
25601             }
25602             
25603             st.store.loadData(avs);
25604             st.collapse();
25605             st.setValue(cn);
25606         }
25607     },
25608     
25609      
25610     updateFooter : function(ans)
25611     {
25612         var html = '';
25613         if (ans === false) {
25614             this.footDisp.dom.innerHTML = '';
25615             return;
25616         }
25617         
25618         this.footerEls = ans.reverse();
25619         Roo.each(this.footerEls, function(a,i) {
25620             if (!a) { return; }
25621             html += html.length ? ' &gt; '  :  '';
25622             
25623             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
25624             
25625         });
25626        
25627         // 
25628         var sz = this.footDisp.up('td').getSize();
25629         this.footDisp.dom.style.width = (sz.width -10) + 'px';
25630         this.footDisp.dom.style.marginLeft = '5px';
25631         
25632         this.footDisp.dom.style.overflow = 'hidden';
25633         
25634         this.footDisp.dom.innerHTML = html;
25635             
25636         
25637     },
25638    
25639        
25640     // private
25641     onDestroy : function(){
25642         if(this.rendered){
25643             
25644             this.tb.items.each(function(item){
25645                 if(item.menu){
25646                     item.menu.removeAll();
25647                     if(item.menu.el){
25648                         item.menu.el.destroy();
25649                     }
25650                 }
25651                 item.destroy();
25652             });
25653              
25654         }
25655     },
25656     onFirstFocus: function() {
25657         // need to do this for all the toolbars..
25658         this.tb.items.each(function(item){
25659            item.enable();
25660         });
25661     },
25662     buildToolbar: function(tlist, nm, friendly_name, block)
25663     {
25664         var editor = this.editor;
25665         var editorcore = this.editorcore;
25666          // create a new element.
25667         var wdiv = editor.wrap.createChild({
25668                 tag: 'div'
25669             }, editor.wrap.dom.firstChild.nextSibling, true);
25670         
25671        
25672         var tb = new Roo.Toolbar(wdiv);
25673         this.tb = tb;
25674         if (tlist === false && block) {
25675             tlist = block.contextMenu(this);
25676         }
25677         
25678         tb.hasStyles = false;
25679         tb.name = nm;
25680         
25681         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
25682         
25683         var styles = Array.from(this.styles);
25684         
25685         
25686         // styles...
25687         if (styles && styles.length) {
25688             tb.hasStyles = true;
25689             // this needs a multi-select checkbox...
25690             tb.addField( new Roo.form.ComboBox({
25691                 store: new Roo.data.SimpleStore({
25692                     id : 'val',
25693                     fields: ['val', 'selected'],
25694                     data : [] 
25695                 }),
25696                 name : '-roo-edit-className',
25697                 attrname : 'className',
25698                 displayField: 'val',
25699                 typeAhead: false,
25700                 mode: 'local',
25701                 editable : false,
25702                 triggerAction: 'all',
25703                 emptyText:'Select Style',
25704                 selectOnFocus:true,
25705                 width: 130,
25706                 listeners : {
25707                     'select': function(c, r, i) {
25708                         // initial support only for on class per el..
25709                         tb.selectedNode.className =  r ? r.get('val') : '';
25710                         editorcore.syncValue();
25711                     }
25712                 }
25713     
25714             }));
25715         }
25716         
25717         var tbc = Roo.form.HtmlEditor.ToolbarContext;
25718         
25719         
25720         for (var i in tlist) {
25721             
25722             // newer versions will use xtype cfg to create menus.
25723             if (typeof(tlist[i].xtype) != 'undefined') {
25724                 
25725                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
25726                 
25727                 
25728                 continue;
25729             }
25730             
25731             var item = tlist[i];
25732             tb.add(item.title + ":&nbsp;");
25733             
25734             
25735             //optname == used so you can configure the options available..
25736             var opts = item.opts ? item.opts : false;
25737             if (item.optname) { // use the b
25738                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
25739            
25740             }
25741             
25742             if (opts) {
25743                 // opts == pulldown..
25744                 tb.addField( new Roo.form.ComboBox({
25745                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
25746                         id : 'val',
25747                         fields: ['val', 'display'],
25748                         data : opts  
25749                     }),
25750                     name : '-roo-edit-' + i,
25751                     
25752                     attrname : i,
25753                     stylename : item.style ? item.style : false,
25754                     
25755                     displayField: item.displayField ? item.displayField : 'val',
25756                     valueField :  'val',
25757                     typeAhead: false,
25758                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
25759                     editable : false,
25760                     triggerAction: 'all',
25761                     emptyText:'Select',
25762                     selectOnFocus:true,
25763                     width: item.width ? item.width  : 130,
25764                     listeners : {
25765                         'select': function(c, r, i) {
25766                             if (tb.selectedNode.hasAttribute('data-block')) {
25767                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25768                                 b[c.attrname] = r.get('val');
25769                                 b.updateElement(tb.selectedNode);
25770                                 editorcore.syncValue();
25771                                 return;
25772                             }
25773                             
25774                             if (c.stylename) {
25775                                 tb.selectedNode.style[c.stylename] =  r.get('val');
25776                                 editorcore.syncValue();
25777                                 return;
25778                             }
25779                             if (r === false) {
25780                                 tb.selectedNode.removeAttribute(c.attrname);
25781                                 editorcore.syncValue();
25782                                 return;
25783                             }
25784                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
25785                             editorcore.syncValue();
25786                         }
25787                     }
25788
25789                 }));
25790                 continue;
25791                     
25792                  
25793                 /*
25794                 tb.addField( new Roo.form.TextField({
25795                     name: i,
25796                     width: 100,
25797                     //allowBlank:false,
25798                     value: ''
25799                 }));
25800                 continue;
25801                 */
25802             }
25803             tb.addField( new Roo.form.TextField({
25804                 name: '-roo-edit-' + i,
25805                 attrname : i,
25806                 
25807                 width: item.width,
25808                 //allowBlank:true,
25809                 value: '',
25810                 listeners: {
25811                     'change' : function(f, nv, ov) {
25812                         
25813                         if (tb.selectedNode.hasAttribute('data-block')) {
25814                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
25815                             b[f.attrname] = nv;
25816                             b.updateElement(tb.selectedNode);
25817                             editorcore.syncValue();
25818                             return;
25819                         }
25820                         
25821                         tb.selectedNode.setAttribute(f.attrname, nv);
25822                         editorcore.syncValue();
25823                     }
25824                 }
25825             }));
25826              
25827         }
25828         
25829         var _this = this;
25830         
25831         if(nm == 'BODY'){
25832             tb.addSeparator();
25833         
25834             tb.addButton( {
25835                 text: 'Stylesheets',
25836
25837                 listeners : {
25838                     click : function ()
25839                     {
25840                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
25841                     }
25842                 }
25843             });
25844         }
25845         
25846         tb.addFill();
25847         tb.addButton({
25848             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
25849     
25850             listeners : {
25851                 click : function ()
25852                 {
25853                     // remove
25854                     // undo does not work.
25855                     var sn = tb.selectedNode;
25856                     if (!sn) {
25857                         return;
25858                     }
25859                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
25860                     if (sn.hasAttribute('data-block')) {
25861                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
25862                         sn.parentNode.removeChild(sn);
25863                         
25864                     } else if (sn && sn.tagName != 'BODY') {
25865                         // remove and keep parents.
25866                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
25867                         a.removeTag(sn);
25868                     }
25869                     
25870                     
25871                     var range = editorcore.createRange();
25872         
25873                     range.setStart(stn,0);
25874                     range.setEnd(stn,0); 
25875                     var selection = editorcore.getSelection();
25876                     selection.removeAllRanges();
25877                     selection.addRange(range);
25878                     
25879                     
25880                     //_this.updateToolbar(null, null, pn);
25881                     _this.updateToolbar(null, null, null);
25882                     _this.updateFooter(false);
25883                     
25884                 }
25885             }
25886             
25887                     
25888                 
25889             
25890         });
25891         
25892         
25893         tb.el.on('click', function(e){
25894             e.preventDefault(); // what does this do?
25895         });
25896         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25897         tb.el.hide();
25898         
25899         // dont need to disable them... as they will get hidden
25900         return tb;
25901          
25902         
25903     },
25904     buildFooter : function()
25905     {
25906         
25907         var fel = this.editor.wrap.createChild();
25908         this.footer = new Roo.Toolbar(fel);
25909         // toolbar has scrolly on left / right?
25910         var footDisp= new Roo.Toolbar.Fill();
25911         var _t = this;
25912         this.footer.add(
25913             {
25914                 text : '&lt;',
25915                 xtype: 'Button',
25916                 handler : function() {
25917                     _t.footDisp.scrollTo('left',0,true)
25918                 }
25919             }
25920         );
25921         this.footer.add( footDisp );
25922         this.footer.add( 
25923             {
25924                 text : '&gt;',
25925                 xtype: 'Button',
25926                 handler : function() {
25927                     // no animation..
25928                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
25929                 }
25930             }
25931         );
25932         var fel = Roo.get(footDisp.el);
25933         fel.addClass('x-editor-context');
25934         this.footDispWrap = fel; 
25935         this.footDispWrap.overflow  = 'hidden';
25936         
25937         this.footDisp = fel.createChild();
25938         this.footDispWrap.on('click', this.onContextClick, this)
25939         
25940         
25941     },
25942     // when the footer contect changes
25943     onContextClick : function (ev,dom)
25944     {
25945         ev.preventDefault();
25946         var  cn = dom.className;
25947         //Roo.log(cn);
25948         if (!cn.match(/x-ed-loc-/)) {
25949             return;
25950         }
25951         var n = cn.split('-').pop();
25952         var ans = this.footerEls;
25953         var sel = ans[n];
25954         
25955          // pick
25956         var range = this.editorcore.createRange();
25957         
25958         range.selectNodeContents(sel);
25959         //range.selectNode(sel);
25960         
25961         
25962         var selection = this.editorcore.getSelection();
25963         selection.removeAllRanges();
25964         selection.addRange(range);
25965         
25966         
25967         
25968         this.updateToolbar(null, null, sel);
25969         
25970         
25971     }
25972     
25973     
25974     
25975     
25976     
25977 });
25978
25979
25980
25981
25982
25983 /*
25984  * Based on:
25985  * Ext JS Library 1.1.1
25986  * Copyright(c) 2006-2007, Ext JS, LLC.
25987  *
25988  * Originally Released Under LGPL - original licence link has changed is not relivant.
25989  *
25990  * Fork - LGPL
25991  * <script type="text/javascript">
25992  */
25993  
25994 /**
25995  * @class Roo.form.BasicForm
25996  * @extends Roo.util.Observable
25997  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25998  * @constructor
25999  * @param {String/HTMLElement/Roo.Element} el The form element or its id
26000  * @param {Object} config Configuration options
26001  */
26002 Roo.form.BasicForm = function(el, config){
26003     this.allItems = [];
26004     this.childForms = [];
26005     Roo.apply(this, config);
26006     /*
26007      * The Roo.form.Field items in this form.
26008      * @type MixedCollection
26009      */
26010      
26011      
26012     this.items = new Roo.util.MixedCollection(false, function(o){
26013         return o.id || (o.id = Roo.id());
26014     });
26015     this.addEvents({
26016         /**
26017          * @event beforeaction
26018          * Fires before any action is performed. Return false to cancel the action.
26019          * @param {Form} this
26020          * @param {Action} action The action to be performed
26021          */
26022         beforeaction: true,
26023         /**
26024          * @event actionfailed
26025          * Fires when an action fails.
26026          * @param {Form} this
26027          * @param {Action} action The action that failed
26028          */
26029         actionfailed : true,
26030         /**
26031          * @event actioncomplete
26032          * Fires when an action is completed.
26033          * @param {Form} this
26034          * @param {Action} action The action that completed
26035          */
26036         actioncomplete : true
26037     });
26038     if(el){
26039         this.initEl(el);
26040     }
26041     Roo.form.BasicForm.superclass.constructor.call(this);
26042     
26043     Roo.form.BasicForm.popover.apply();
26044 };
26045
26046 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
26047     /**
26048      * @cfg {String} method
26049      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
26050      */
26051     /**
26052      * @cfg {DataReader} reader
26053      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26054      * This is optional as there is built-in support for processing JSON.
26055      */
26056     /**
26057      * @cfg {DataReader} errorReader
26058      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26059      * This is completely optional as there is built-in support for processing JSON.
26060      */
26061     /**
26062      * @cfg {String} url
26063      * The URL to use for form actions if one isn't supplied in the action options.
26064      */
26065     /**
26066      * @cfg {Boolean} fileUpload
26067      * Set to true if this form is a file upload.
26068      */
26069      
26070     /**
26071      * @cfg {Object} baseParams
26072      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26073      */
26074      /**
26075      
26076     /**
26077      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26078      */
26079     timeout: 30,
26080
26081     // private
26082     activeAction : null,
26083
26084     /**
26085      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26086      * or setValues() data instead of when the form was first created.
26087      */
26088     trackResetOnLoad : false,
26089     
26090     
26091     /**
26092      * childForms - used for multi-tab forms
26093      * @type {Array}
26094      */
26095     childForms : false,
26096     
26097     /**
26098      * allItems - full list of fields.
26099      * @type {Array}
26100      */
26101     allItems : false,
26102     
26103     /**
26104      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26105      * element by passing it or its id or mask the form itself by passing in true.
26106      * @type Mixed
26107      */
26108     waitMsgTarget : false,
26109     
26110     /**
26111      * @type Boolean
26112      */
26113     disableMask : false,
26114     
26115     /**
26116      * @cfg {Boolean} errorMask (true|false) default false
26117      */
26118     errorMask : false,
26119     
26120     /**
26121      * @cfg {Number} maskOffset Default 100
26122      */
26123     maskOffset : 100,
26124
26125     // private
26126     initEl : function(el){
26127         this.el = Roo.get(el);
26128         this.id = this.el.id || Roo.id();
26129         this.el.on('submit', this.onSubmit, this);
26130         this.el.addClass('x-form');
26131     },
26132
26133     // private
26134     onSubmit : function(e){
26135         e.stopEvent();
26136     },
26137
26138     /**
26139      * Returns true if client-side validation on the form is successful.
26140      * @return Boolean
26141      */
26142     isValid : function(){
26143         var valid = true;
26144         var target = false;
26145         this.items.each(function(f){
26146             if(f.validate()){
26147                 return;
26148             }
26149             
26150             valid = false;
26151                 
26152             if(!target && f.el.isVisible(true)){
26153                 target = f;
26154             }
26155         });
26156         
26157         if(this.errorMask && !valid){
26158             Roo.form.BasicForm.popover.mask(this, target);
26159         }
26160         
26161         return valid;
26162     },
26163     /**
26164      * Returns array of invalid form fields.
26165      * @return Array
26166      */
26167     
26168     invalidFields : function()
26169     {
26170         var ret = [];
26171         this.items.each(function(f){
26172             if(f.validate()){
26173                 return;
26174             }
26175             ret.push(f);
26176             
26177         });
26178         
26179         return ret;
26180     },
26181     
26182     
26183     /**
26184      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
26185      * @return Boolean
26186      */
26187     isDirty : function(){
26188         var dirty = false;
26189         this.items.each(function(f){
26190            if(f.isDirty()){
26191                dirty = true;
26192                return false;
26193            }
26194         });
26195         return dirty;
26196     },
26197     
26198     /**
26199      * Returns true if any fields in this form have changed since their original load. (New version)
26200      * @return Boolean
26201      */
26202     
26203     hasChanged : function()
26204     {
26205         var dirty = false;
26206         this.items.each(function(f){
26207            if(f.hasChanged()){
26208                dirty = true;
26209                return false;
26210            }
26211         });
26212         return dirty;
26213         
26214     },
26215     /**
26216      * Resets all hasChanged to 'false' -
26217      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
26218      * So hasChanged storage is only to be used for this purpose
26219      * @return Boolean
26220      */
26221     resetHasChanged : function()
26222     {
26223         this.items.each(function(f){
26224            f.resetHasChanged();
26225         });
26226         
26227     },
26228     
26229     
26230     /**
26231      * Performs a predefined action (submit or load) or custom actions you define on this form.
26232      * @param {String} actionName The name of the action type
26233      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26234      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26235      * accept other config options):
26236      * <pre>
26237 Property          Type             Description
26238 ----------------  ---------------  ----------------------------------------------------------------------------------
26239 url               String           The url for the action (defaults to the form's url)
26240 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26241 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26242 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26243                                    validate the form on the client (defaults to false)
26244      * </pre>
26245      * @return {BasicForm} this
26246      */
26247     doAction : function(action, options){
26248         if(typeof action == 'string'){
26249             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26250         }
26251         if(this.fireEvent('beforeaction', this, action) !== false){
26252             this.beforeAction(action);
26253             action.run.defer(100, action);
26254         }
26255         return this;
26256     },
26257
26258     /**
26259      * Shortcut to do a submit action.
26260      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26261      * @return {BasicForm} this
26262      */
26263     submit : function(options){
26264         this.doAction('submit', options);
26265         return this;
26266     },
26267
26268     /**
26269      * Shortcut to do a load action.
26270      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26271      * @return {BasicForm} this
26272      */
26273     load : function(options){
26274         this.doAction('load', options);
26275         return this;
26276     },
26277
26278     /**
26279      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26280      * @param {Record} record The record to edit
26281      * @return {BasicForm} this
26282      */
26283     updateRecord : function(record){
26284         record.beginEdit();
26285         var fs = record.fields;
26286         fs.each(function(f){
26287             var field = this.findField(f.name);
26288             if(field){
26289                 record.set(f.name, field.getValue());
26290             }
26291         }, this);
26292         record.endEdit();
26293         return this;
26294     },
26295
26296     /**
26297      * Loads an Roo.data.Record into this form.
26298      * @param {Record} record The record to load
26299      * @return {BasicForm} this
26300      */
26301     loadRecord : function(record){
26302         this.setValues(record.data);
26303         return this;
26304     },
26305
26306     // private
26307     beforeAction : function(action){
26308         var o = action.options;
26309         
26310         if(!this.disableMask) {
26311             if(this.waitMsgTarget === true){
26312                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26313             }else if(this.waitMsgTarget){
26314                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26315                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26316             }else {
26317                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26318             }
26319         }
26320         
26321          
26322     },
26323
26324     // private
26325     afterAction : function(action, success){
26326         this.activeAction = null;
26327         var o = action.options;
26328         
26329         if(!this.disableMask) {
26330             if(this.waitMsgTarget === true){
26331                 this.el.unmask();
26332             }else if(this.waitMsgTarget){
26333                 this.waitMsgTarget.unmask();
26334             }else{
26335                 Roo.MessageBox.updateProgress(1);
26336                 Roo.MessageBox.hide();
26337             }
26338         }
26339         
26340         if(success){
26341             if(o.reset){
26342                 this.reset();
26343             }
26344             Roo.callback(o.success, o.scope, [this, action]);
26345             this.fireEvent('actioncomplete', this, action);
26346             
26347         }else{
26348             
26349             // failure condition..
26350             // we have a scenario where updates need confirming.
26351             // eg. if a locking scenario exists..
26352             // we look for { errors : { needs_confirm : true }} in the response.
26353             if (
26354                 (typeof(action.result) != 'undefined')  &&
26355                 (typeof(action.result.errors) != 'undefined')  &&
26356                 (typeof(action.result.errors.needs_confirm) != 'undefined')
26357            ){
26358                 var _t = this;
26359                 Roo.MessageBox.confirm(
26360                     "Change requires confirmation",
26361                     action.result.errorMsg,
26362                     function(r) {
26363                         if (r != 'yes') {
26364                             return;
26365                         }
26366                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
26367                     }
26368                     
26369                 );
26370                 
26371                 
26372                 
26373                 return;
26374             }
26375             
26376             Roo.callback(o.failure, o.scope, [this, action]);
26377             // show an error message if no failed handler is set..
26378             if (!this.hasListener('actionfailed')) {
26379                 Roo.MessageBox.alert("Error",
26380                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
26381                         action.result.errorMsg :
26382                         "Saving Failed, please check your entries or try again"
26383                 );
26384             }
26385             
26386             this.fireEvent('actionfailed', this, action);
26387         }
26388         
26389     },
26390
26391     /**
26392      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26393      * @param {String} id The value to search for
26394      * @return Field
26395      */
26396     findField : function(id){
26397         var field = this.items.get(id);
26398         if(!field){
26399             this.items.each(function(f){
26400                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26401                     field = f;
26402                     return false;
26403                 }
26404             });
26405         }
26406         return field || null;
26407     },
26408
26409     /**
26410      * Add a secondary form to this one, 
26411      * Used to provide tabbed forms. One form is primary, with hidden values 
26412      * which mirror the elements from the other forms.
26413      * 
26414      * @param {Roo.form.Form} form to add.
26415      * 
26416      */
26417     addForm : function(form)
26418     {
26419        
26420         if (this.childForms.indexOf(form) > -1) {
26421             // already added..
26422             return;
26423         }
26424         this.childForms.push(form);
26425         var n = '';
26426         Roo.each(form.allItems, function (fe) {
26427             
26428             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26429             if (this.findField(n)) { // already added..
26430                 return;
26431             }
26432             var add = new Roo.form.Hidden({
26433                 name : n
26434             });
26435             add.render(this.el);
26436             
26437             this.add( add );
26438         }, this);
26439         
26440     },
26441     /**
26442      * Mark fields in this form invalid in bulk.
26443      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26444      * @return {BasicForm} this
26445      */
26446     markInvalid : function(errors){
26447         if(errors instanceof Array){
26448             for(var i = 0, len = errors.length; i < len; i++){
26449                 var fieldError = errors[i];
26450                 var f = this.findField(fieldError.id);
26451                 if(f){
26452                     f.markInvalid(fieldError.msg);
26453                 }
26454             }
26455         }else{
26456             var field, id;
26457             for(id in errors){
26458                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26459                     field.markInvalid(errors[id]);
26460                 }
26461             }
26462         }
26463         Roo.each(this.childForms || [], function (f) {
26464             f.markInvalid(errors);
26465         });
26466         
26467         return this;
26468     },
26469
26470     /**
26471      * Set values for fields in this form in bulk.
26472      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26473      * @return {BasicForm} this
26474      */
26475     setValues : function(values){
26476         if(values instanceof Array){ // array of objects
26477             for(var i = 0, len = values.length; i < len; i++){
26478                 var v = values[i];
26479                 var f = this.findField(v.id);
26480                 if(f){
26481                     f.setValue(v.value);
26482                     if(this.trackResetOnLoad){
26483                         f.originalValue = f.getValue();
26484                     }
26485                 }
26486             }
26487         }else{ // object hash
26488             var field, id;
26489             for(id in values){
26490                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26491                     
26492                     if (field.setFromData && 
26493                         field.valueField && 
26494                         field.displayField &&
26495                         // combos' with local stores can 
26496                         // be queried via setValue()
26497                         // to set their value..
26498                         (field.store && !field.store.isLocal)
26499                         ) {
26500                         // it's a combo
26501                         var sd = { };
26502                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26503                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26504                         field.setFromData(sd);
26505                         
26506                     } else {
26507                         field.setValue(values[id]);
26508                     }
26509                     
26510                     
26511                     if(this.trackResetOnLoad){
26512                         field.originalValue = field.getValue();
26513                     }
26514                 }
26515             }
26516         }
26517         this.resetHasChanged();
26518         
26519         
26520         Roo.each(this.childForms || [], function (f) {
26521             f.setValues(values);
26522             f.resetHasChanged();
26523         });
26524                 
26525         return this;
26526     },
26527  
26528     /**
26529      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26530      * they are returned as an array.
26531      * @param {Boolean} asString
26532      * @return {Object}
26533      */
26534     getValues : function(asString){
26535         if (this.childForms) {
26536             // copy values from the child forms
26537             Roo.each(this.childForms, function (f) {
26538                 this.setValues(f.getValues());
26539             }, this);
26540         }
26541         
26542         // use formdata
26543         if (typeof(FormData) != 'undefined' && asString !== true) {
26544             // this relies on a 'recent' version of chrome apparently...
26545             try {
26546                 var fd = (new FormData(this.el.dom)).entries();
26547                 var ret = {};
26548                 var ent = fd.next();
26549                 while (!ent.done) {
26550                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
26551                     ent = fd.next();
26552                 };
26553                 return ret;
26554             } catch(e) {
26555                 
26556             }
26557             
26558         }
26559         
26560         
26561         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26562         if(asString === true){
26563             return fs;
26564         }
26565         return Roo.urlDecode(fs);
26566     },
26567     
26568     /**
26569      * Returns the fields in this form as an object with key/value pairs. 
26570      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26571      * @return {Object}
26572      */
26573     getFieldValues : function(with_hidden)
26574     {
26575         if (this.childForms) {
26576             // copy values from the child forms
26577             // should this call getFieldValues - probably not as we do not currently copy
26578             // hidden fields when we generate..
26579             Roo.each(this.childForms, function (f) {
26580                 this.setValues(f.getValues());
26581             }, this);
26582         }
26583         
26584         var ret = {};
26585         this.items.each(function(f){
26586             if (!f.getName()) {
26587                 return;
26588             }
26589             var v = f.getValue();
26590             if (f.inputType =='radio') {
26591                 if (typeof(ret[f.getName()]) == 'undefined') {
26592                     ret[f.getName()] = ''; // empty..
26593                 }
26594                 
26595                 if (!f.el.dom.checked) {
26596                     return;
26597                     
26598                 }
26599                 v = f.el.dom.value;
26600                 
26601             }
26602             
26603             // not sure if this supported any more..
26604             if ((typeof(v) == 'object') && f.getRawValue) {
26605                 v = f.getRawValue() ; // dates..
26606             }
26607             // combo boxes where name != hiddenName...
26608             if (f.name != f.getName()) {
26609                 ret[f.name] = f.getRawValue();
26610             }
26611             ret[f.getName()] = v;
26612         });
26613         
26614         return ret;
26615     },
26616
26617     /**
26618      * Clears all invalid messages in this form.
26619      * @return {BasicForm} this
26620      */
26621     clearInvalid : function(){
26622         this.items.each(function(f){
26623            f.clearInvalid();
26624         });
26625         
26626         Roo.each(this.childForms || [], function (f) {
26627             f.clearInvalid();
26628         });
26629         
26630         
26631         return this;
26632     },
26633
26634     /**
26635      * Resets this form.
26636      * @return {BasicForm} this
26637      */
26638     reset : function(){
26639         this.items.each(function(f){
26640             f.reset();
26641         });
26642         
26643         Roo.each(this.childForms || [], function (f) {
26644             f.reset();
26645         });
26646         this.resetHasChanged();
26647         
26648         return this;
26649     },
26650
26651     /**
26652      * Add Roo.form components to this form.
26653      * @param {Field} field1
26654      * @param {Field} field2 (optional)
26655      * @param {Field} etc (optional)
26656      * @return {BasicForm} this
26657      */
26658     add : function(){
26659         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26660         return this;
26661     },
26662
26663
26664     /**
26665      * Removes a field from the items collection (does NOT remove its markup).
26666      * @param {Field} field
26667      * @return {BasicForm} this
26668      */
26669     remove : function(field){
26670         this.items.remove(field);
26671         return this;
26672     },
26673
26674     /**
26675      * Looks at the fields in this form, checks them for an id attribute,
26676      * and calls applyTo on the existing dom element with that id.
26677      * @return {BasicForm} this
26678      */
26679     render : function(){
26680         this.items.each(function(f){
26681             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26682                 f.applyTo(f.id);
26683             }
26684         });
26685         return this;
26686     },
26687
26688     /**
26689      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26690      * @param {Object} values
26691      * @return {BasicForm} this
26692      */
26693     applyToFields : function(o){
26694         this.items.each(function(f){
26695            Roo.apply(f, o);
26696         });
26697         return this;
26698     },
26699
26700     /**
26701      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26702      * @param {Object} values
26703      * @return {BasicForm} this
26704      */
26705     applyIfToFields : function(o){
26706         this.items.each(function(f){
26707            Roo.applyIf(f, o);
26708         });
26709         return this;
26710     }
26711 });
26712
26713 // back compat
26714 Roo.BasicForm = Roo.form.BasicForm;
26715
26716 Roo.apply(Roo.form.BasicForm, {
26717     
26718     popover : {
26719         
26720         padding : 5,
26721         
26722         isApplied : false,
26723         
26724         isMasked : false,
26725         
26726         form : false,
26727         
26728         target : false,
26729         
26730         intervalID : false,
26731         
26732         maskEl : false,
26733         
26734         apply : function()
26735         {
26736             if(this.isApplied){
26737                 return;
26738             }
26739             
26740             this.maskEl = {
26741                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
26742                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
26743                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
26744                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
26745             };
26746             
26747             this.maskEl.top.enableDisplayMode("block");
26748             this.maskEl.left.enableDisplayMode("block");
26749             this.maskEl.bottom.enableDisplayMode("block");
26750             this.maskEl.right.enableDisplayMode("block");
26751             
26752             Roo.get(document.body).on('click', function(){
26753                 this.unmask();
26754             }, this);
26755             
26756             Roo.get(document.body).on('touchstart', function(){
26757                 this.unmask();
26758             }, this);
26759             
26760             this.isApplied = true
26761         },
26762         
26763         mask : function(form, target)
26764         {
26765             this.form = form;
26766             
26767             this.target = target;
26768             
26769             if(!this.form.errorMask || !target.el){
26770                 return;
26771             }
26772             
26773             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
26774             
26775             var ot = this.target.el.calcOffsetsTo(scrollable);
26776             
26777             var scrollTo = ot[1] - this.form.maskOffset;
26778             
26779             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
26780             
26781             scrollable.scrollTo('top', scrollTo);
26782             
26783             var el = this.target.wrap || this.target.el;
26784             
26785             var box = el.getBox();
26786             
26787             this.maskEl.top.setStyle('position', 'absolute');
26788             this.maskEl.top.setStyle('z-index', 10000);
26789             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
26790             this.maskEl.top.setLeft(0);
26791             this.maskEl.top.setTop(0);
26792             this.maskEl.top.show();
26793             
26794             this.maskEl.left.setStyle('position', 'absolute');
26795             this.maskEl.left.setStyle('z-index', 10000);
26796             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
26797             this.maskEl.left.setLeft(0);
26798             this.maskEl.left.setTop(box.y - this.padding);
26799             this.maskEl.left.show();
26800
26801             this.maskEl.bottom.setStyle('position', 'absolute');
26802             this.maskEl.bottom.setStyle('z-index', 10000);
26803             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
26804             this.maskEl.bottom.setLeft(0);
26805             this.maskEl.bottom.setTop(box.bottom + this.padding);
26806             this.maskEl.bottom.show();
26807
26808             this.maskEl.right.setStyle('position', 'absolute');
26809             this.maskEl.right.setStyle('z-index', 10000);
26810             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
26811             this.maskEl.right.setLeft(box.right + this.padding);
26812             this.maskEl.right.setTop(box.y - this.padding);
26813             this.maskEl.right.show();
26814
26815             this.intervalID = window.setInterval(function() {
26816                 Roo.form.BasicForm.popover.unmask();
26817             }, 10000);
26818
26819             window.onwheel = function(){ return false;};
26820             
26821             (function(){ this.isMasked = true; }).defer(500, this);
26822             
26823         },
26824         
26825         unmask : function()
26826         {
26827             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
26828                 return;
26829             }
26830             
26831             this.maskEl.top.setStyle('position', 'absolute');
26832             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
26833             this.maskEl.top.hide();
26834
26835             this.maskEl.left.setStyle('position', 'absolute');
26836             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
26837             this.maskEl.left.hide();
26838
26839             this.maskEl.bottom.setStyle('position', 'absolute');
26840             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
26841             this.maskEl.bottom.hide();
26842
26843             this.maskEl.right.setStyle('position', 'absolute');
26844             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
26845             this.maskEl.right.hide();
26846             
26847             window.onwheel = function(){ return true;};
26848             
26849             if(this.intervalID){
26850                 window.clearInterval(this.intervalID);
26851                 this.intervalID = false;
26852             }
26853             
26854             this.isMasked = false;
26855             
26856         }
26857         
26858     }
26859     
26860 });/*
26861  * Based on:
26862  * Ext JS Library 1.1.1
26863  * Copyright(c) 2006-2007, Ext JS, LLC.
26864  *
26865  * Originally Released Under LGPL - original licence link has changed is not relivant.
26866  *
26867  * Fork - LGPL
26868  * <script type="text/javascript">
26869  */
26870
26871 /**
26872  * @class Roo.form.Form
26873  * @extends Roo.form.BasicForm
26874  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26875  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26876  * @constructor
26877  * @param {Object} config Configuration options
26878  */
26879 Roo.form.Form = function(config){
26880     var xitems =  [];
26881     if (config.items) {
26882         xitems = config.items;
26883         delete config.items;
26884     }
26885    
26886     
26887     Roo.form.Form.superclass.constructor.call(this, null, config);
26888     this.url = this.url || this.action;
26889     if(!this.root){
26890         this.root = new Roo.form.Layout(Roo.applyIf({
26891             id: Roo.id()
26892         }, config));
26893     }
26894     this.active = this.root;
26895     /**
26896      * Array of all the buttons that have been added to this form via {@link addButton}
26897      * @type Array
26898      */
26899     this.buttons = [];
26900     this.allItems = [];
26901     this.addEvents({
26902         /**
26903          * @event clientvalidation
26904          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26905          * @param {Form} this
26906          * @param {Boolean} valid true if the form has passed client-side validation
26907          */
26908         clientvalidation: true,
26909         /**
26910          * @event rendered
26911          * Fires when the form is rendered
26912          * @param {Roo.form.Form} form
26913          */
26914         rendered : true
26915     });
26916     
26917     if (this.progressUrl) {
26918             // push a hidden field onto the list of fields..
26919             this.addxtype( {
26920                     xns: Roo.form, 
26921                     xtype : 'Hidden', 
26922                     name : 'UPLOAD_IDENTIFIER' 
26923             });
26924         }
26925         
26926     
26927     Roo.each(xitems, this.addxtype, this);
26928     
26929 };
26930
26931 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26932      /**
26933      * @cfg {Roo.Button} buttons[] buttons at bottom of form
26934      */
26935     
26936     /**
26937      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26938      */
26939     /**
26940      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26941      */
26942     /**
26943      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26944      */
26945     buttonAlign:'center',
26946
26947     /**
26948      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26949      */
26950     minButtonWidth:75,
26951
26952     /**
26953      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26954      * This property cascades to child containers if not set.
26955      */
26956     labelAlign:'left',
26957
26958     /**
26959      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26960      * fires a looping event with that state. This is required to bind buttons to the valid
26961      * state using the config value formBind:true on the button.
26962      */
26963     monitorValid : false,
26964
26965     /**
26966      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26967      */
26968     monitorPoll : 200,
26969     
26970     /**
26971      * @cfg {String} progressUrl - Url to return progress data 
26972      */
26973     
26974     progressUrl : false,
26975     /**
26976      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
26977      * sending a formdata with extra parameters - eg uploaded elements.
26978      */
26979     
26980     formData : false,
26981     
26982     /**
26983      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26984      * fields are added and the column is closed. If no fields are passed the column remains open
26985      * until end() is called.
26986      * @param {Object} config The config to pass to the column
26987      * @param {Field} field1 (optional)
26988      * @param {Field} field2 (optional)
26989      * @param {Field} etc (optional)
26990      * @return Column The column container object
26991      */
26992     column : function(c){
26993         var col = new Roo.form.Column(c);
26994         this.start(col);
26995         if(arguments.length > 1){ // duplicate code required because of Opera
26996             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26997             this.end();
26998         }
26999         return col;
27000     },
27001
27002     /**
27003      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27004      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27005      * until end() is called.
27006      * @param {Object} config The config to pass to the fieldset
27007      * @param {Field} field1 (optional)
27008      * @param {Field} field2 (optional)
27009      * @param {Field} etc (optional)
27010      * @return FieldSet The fieldset container object
27011      */
27012     fieldset : function(c){
27013         var fs = new Roo.form.FieldSet(c);
27014         this.start(fs);
27015         if(arguments.length > 1){ // duplicate code required because of Opera
27016             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27017             this.end();
27018         }
27019         return fs;
27020     },
27021
27022     /**
27023      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27024      * fields are added and the container is closed. If no fields are passed the container remains open
27025      * until end() is called.
27026      * @param {Object} config The config to pass to the Layout
27027      * @param {Field} field1 (optional)
27028      * @param {Field} field2 (optional)
27029      * @param {Field} etc (optional)
27030      * @return Layout The container object
27031      */
27032     container : function(c){
27033         var l = new Roo.form.Layout(c);
27034         this.start(l);
27035         if(arguments.length > 1){ // duplicate code required because of Opera
27036             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27037             this.end();
27038         }
27039         return l;
27040     },
27041
27042     /**
27043      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27044      * @param {Object} container A Roo.form.Layout or subclass of Layout
27045      * @return {Form} this
27046      */
27047     start : function(c){
27048         // cascade label info
27049         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27050         this.active.stack.push(c);
27051         c.ownerCt = this.active;
27052         this.active = c;
27053         return this;
27054     },
27055
27056     /**
27057      * Closes the current open container
27058      * @return {Form} this
27059      */
27060     end : function(){
27061         if(this.active == this.root){
27062             return this;
27063         }
27064         this.active = this.active.ownerCt;
27065         return this;
27066     },
27067
27068     /**
27069      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
27070      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
27071      * as the label of the field.
27072      * @param {Field} field1
27073      * @param {Field} field2 (optional)
27074      * @param {Field} etc. (optional)
27075      * @return {Form} this
27076      */
27077     add : function(){
27078         this.active.stack.push.apply(this.active.stack, arguments);
27079         this.allItems.push.apply(this.allItems,arguments);
27080         var r = [];
27081         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
27082             if(a[i].isFormField){
27083                 r.push(a[i]);
27084             }
27085         }
27086         if(r.length > 0){
27087             Roo.form.Form.superclass.add.apply(this, r);
27088         }
27089         return this;
27090     },
27091     
27092
27093     
27094     
27095     
27096      /**
27097      * Find any element that has been added to a form, using it's ID or name
27098      * This can include framesets, columns etc. along with regular fields..
27099      * @param {String} id - id or name to find.
27100      
27101      * @return {Element} e - or false if nothing found.
27102      */
27103     findbyId : function(id)
27104     {
27105         var ret = false;
27106         if (!id) {
27107             return ret;
27108         }
27109         Roo.each(this.allItems, function(f){
27110             if (f.id == id || f.name == id ){
27111                 ret = f;
27112                 return false;
27113             }
27114         });
27115         return ret;
27116     },
27117
27118     
27119     
27120     /**
27121      * Render this form into the passed container. This should only be called once!
27122      * @param {String/HTMLElement/Element} container The element this component should be rendered into
27123      * @return {Form} this
27124      */
27125     render : function(ct)
27126     {
27127         
27128         
27129         
27130         ct = Roo.get(ct);
27131         var o = this.autoCreate || {
27132             tag: 'form',
27133             method : this.method || 'POST',
27134             id : this.id || Roo.id()
27135         };
27136         this.initEl(ct.createChild(o));
27137
27138         this.root.render(this.el);
27139         
27140        
27141              
27142         this.items.each(function(f){
27143             f.render('x-form-el-'+f.id);
27144         });
27145
27146         if(this.buttons.length > 0){
27147             // tables are required to maintain order and for correct IE layout
27148             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
27149                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
27150                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
27151             }}, null, true);
27152             var tr = tb.getElementsByTagName('tr')[0];
27153             for(var i = 0, len = this.buttons.length; i < len; i++) {
27154                 var b = this.buttons[i];
27155                 var td = document.createElement('td');
27156                 td.className = 'x-form-btn-td';
27157                 b.render(tr.appendChild(td));
27158             }
27159         }
27160         if(this.monitorValid){ // initialize after render
27161             this.startMonitoring();
27162         }
27163         this.fireEvent('rendered', this);
27164         return this;
27165     },
27166
27167     /**
27168      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
27169      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
27170      * object or a valid Roo.DomHelper element config
27171      * @param {Function} handler The function called when the button is clicked
27172      * @param {Object} scope (optional) The scope of the handler function
27173      * @return {Roo.Button}
27174      */
27175     addButton : function(config, handler, scope){
27176         var bc = {
27177             handler: handler,
27178             scope: scope,
27179             minWidth: this.minButtonWidth,
27180             hideParent:true
27181         };
27182         if(typeof config == "string"){
27183             bc.text = config;
27184         }else{
27185             Roo.apply(bc, config);
27186         }
27187         var btn = new Roo.Button(null, bc);
27188         this.buttons.push(btn);
27189         return btn;
27190     },
27191
27192      /**
27193      * Adds a series of form elements (using the xtype property as the factory method.
27194      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
27195      * @param {Object} config 
27196      */
27197     
27198     addxtype : function()
27199     {
27200         var ar = Array.prototype.slice.call(arguments, 0);
27201         var ret = false;
27202         for(var i = 0; i < ar.length; i++) {
27203             if (!ar[i]) {
27204                 continue; // skip -- if this happends something invalid got sent, we 
27205                 // should ignore it, as basically that interface element will not show up
27206                 // and that should be pretty obvious!!
27207             }
27208             
27209             if (Roo.form[ar[i].xtype]) {
27210                 ar[i].form = this;
27211                 var fe = Roo.factory(ar[i], Roo.form);
27212                 if (!ret) {
27213                     ret = fe;
27214                 }
27215                 fe.form = this;
27216                 if (fe.store) {
27217                     fe.store.form = this;
27218                 }
27219                 if (fe.isLayout) {  
27220                          
27221                     this.start(fe);
27222                     this.allItems.push(fe);
27223                     if (fe.items && fe.addxtype) {
27224                         fe.addxtype.apply(fe, fe.items);
27225                         delete fe.items;
27226                     }
27227                      this.end();
27228                     continue;
27229                 }
27230                 
27231                 
27232                  
27233                 this.add(fe);
27234               //  console.log('adding ' + ar[i].xtype);
27235             }
27236             if (ar[i].xtype == 'Button') {  
27237                 //console.log('adding button');
27238                 //console.log(ar[i]);
27239                 this.addButton(ar[i]);
27240                 this.allItems.push(fe);
27241                 continue;
27242             }
27243             
27244             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
27245                 alert('end is not supported on xtype any more, use items');
27246             //    this.end();
27247             //    //console.log('adding end');
27248             }
27249             
27250         }
27251         return ret;
27252     },
27253     
27254     /**
27255      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
27256      * option "monitorValid"
27257      */
27258     startMonitoring : function(){
27259         if(!this.bound){
27260             this.bound = true;
27261             Roo.TaskMgr.start({
27262                 run : this.bindHandler,
27263                 interval : this.monitorPoll || 200,
27264                 scope: this
27265             });
27266         }
27267     },
27268
27269     /**
27270      * Stops monitoring of the valid state of this form
27271      */
27272     stopMonitoring : function(){
27273         this.bound = false;
27274     },
27275
27276     // private
27277     bindHandler : function(){
27278         if(!this.bound){
27279             return false; // stops binding
27280         }
27281         var valid = true;
27282         this.items.each(function(f){
27283             if(!f.isValid(true)){
27284                 valid = false;
27285                 return false;
27286             }
27287         });
27288         for(var i = 0, len = this.buttons.length; i < len; i++){
27289             var btn = this.buttons[i];
27290             if(btn.formBind === true && btn.disabled === valid){
27291                 btn.setDisabled(!valid);
27292             }
27293         }
27294         this.fireEvent('clientvalidation', this, valid);
27295     }
27296     
27297     
27298     
27299     
27300     
27301     
27302     
27303     
27304 });
27305
27306
27307 // back compat
27308 Roo.Form = Roo.form.Form;
27309 /*
27310  * Based on:
27311  * Ext JS Library 1.1.1
27312  * Copyright(c) 2006-2007, Ext JS, LLC.
27313  *
27314  * Originally Released Under LGPL - original licence link has changed is not relivant.
27315  *
27316  * Fork - LGPL
27317  * <script type="text/javascript">
27318  */
27319
27320 // as we use this in bootstrap.
27321 Roo.namespace('Roo.form');
27322  /**
27323  * @class Roo.form.Action
27324  * Internal Class used to handle form actions
27325  * @constructor
27326  * @param {Roo.form.BasicForm} el The form element or its id
27327  * @param {Object} config Configuration options
27328  */
27329
27330  
27331  
27332 // define the action interface
27333 Roo.form.Action = function(form, options){
27334     this.form = form;
27335     this.options = options || {};
27336 };
27337 /**
27338  * Client Validation Failed
27339  * @const 
27340  */
27341 Roo.form.Action.CLIENT_INVALID = 'client';
27342 /**
27343  * Server Validation Failed
27344  * @const 
27345  */
27346 Roo.form.Action.SERVER_INVALID = 'server';
27347  /**
27348  * Connect to Server Failed
27349  * @const 
27350  */
27351 Roo.form.Action.CONNECT_FAILURE = 'connect';
27352 /**
27353  * Reading Data from Server Failed
27354  * @const 
27355  */
27356 Roo.form.Action.LOAD_FAILURE = 'load';
27357
27358 Roo.form.Action.prototype = {
27359     type : 'default',
27360     failureType : undefined,
27361     response : undefined,
27362     result : undefined,
27363
27364     // interface method
27365     run : function(options){
27366
27367     },
27368
27369     // interface method
27370     success : function(response){
27371
27372     },
27373
27374     // interface method
27375     handleResponse : function(response){
27376
27377     },
27378
27379     // default connection failure
27380     failure : function(response){
27381         
27382         this.response = response;
27383         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27384         this.form.afterAction(this, false);
27385     },
27386
27387     processResponse : function(response){
27388         this.response = response;
27389         if(!response.responseText){
27390             return true;
27391         }
27392         this.result = this.handleResponse(response);
27393         return this.result;
27394     },
27395
27396     // utility functions used internally
27397     getUrl : function(appendParams){
27398         var url = this.options.url || this.form.url || this.form.el.dom.action;
27399         if(appendParams){
27400             var p = this.getParams();
27401             if(p){
27402                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27403             }
27404         }
27405         return url;
27406     },
27407
27408     getMethod : function(){
27409         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27410     },
27411
27412     getParams : function(){
27413         var bp = this.form.baseParams;
27414         var p = this.options.params;
27415         if(p){
27416             if(typeof p == "object"){
27417                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27418             }else if(typeof p == 'string' && bp){
27419                 p += '&' + Roo.urlEncode(bp);
27420             }
27421         }else if(bp){
27422             p = Roo.urlEncode(bp);
27423         }
27424         return p;
27425     },
27426
27427     createCallback : function(){
27428         return {
27429             success: this.success,
27430             failure: this.failure,
27431             scope: this,
27432             timeout: (this.form.timeout*1000),
27433             upload: this.form.fileUpload ? this.success : undefined
27434         };
27435     }
27436 };
27437
27438 Roo.form.Action.Submit = function(form, options){
27439     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27440 };
27441
27442 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27443     type : 'submit',
27444
27445     haveProgress : false,
27446     uploadComplete : false,
27447     
27448     // uploadProgress indicator.
27449     uploadProgress : function()
27450     {
27451         if (!this.form.progressUrl) {
27452             return;
27453         }
27454         
27455         if (!this.haveProgress) {
27456             Roo.MessageBox.progress("Uploading", "Uploading");
27457         }
27458         if (this.uploadComplete) {
27459            Roo.MessageBox.hide();
27460            return;
27461         }
27462         
27463         this.haveProgress = true;
27464    
27465         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27466         
27467         var c = new Roo.data.Connection();
27468         c.request({
27469             url : this.form.progressUrl,
27470             params: {
27471                 id : uid
27472             },
27473             method: 'GET',
27474             success : function(req){
27475                //console.log(data);
27476                 var rdata = false;
27477                 var edata;
27478                 try  {
27479                    rdata = Roo.decode(req.responseText)
27480                 } catch (e) {
27481                     Roo.log("Invalid data from server..");
27482                     Roo.log(edata);
27483                     return;
27484                 }
27485                 if (!rdata || !rdata.success) {
27486                     Roo.log(rdata);
27487                     Roo.MessageBox.alert(Roo.encode(rdata));
27488                     return;
27489                 }
27490                 var data = rdata.data;
27491                 
27492                 if (this.uploadComplete) {
27493                    Roo.MessageBox.hide();
27494                    return;
27495                 }
27496                    
27497                 if (data){
27498                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27499                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27500                     );
27501                 }
27502                 this.uploadProgress.defer(2000,this);
27503             },
27504        
27505             failure: function(data) {
27506                 Roo.log('progress url failed ');
27507                 Roo.log(data);
27508             },
27509             scope : this
27510         });
27511            
27512     },
27513     
27514     
27515     run : function()
27516     {
27517         // run get Values on the form, so it syncs any secondary forms.
27518         this.form.getValues();
27519         
27520         var o = this.options;
27521         var method = this.getMethod();
27522         var isPost = method == 'POST';
27523         if(o.clientValidation === false || this.form.isValid()){
27524             
27525             if (this.form.progressUrl) {
27526                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27527                     (new Date() * 1) + '' + Math.random());
27528                     
27529             } 
27530             
27531             
27532             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27533                 form:this.form.el.dom,
27534                 url:this.getUrl(!isPost),
27535                 method: method,
27536                 params:isPost ? this.getParams() : null,
27537                 isUpload: this.form.fileUpload,
27538                 formData : this.form.formData
27539             }));
27540             
27541             this.uploadProgress();
27542
27543         }else if (o.clientValidation !== false){ // client validation failed
27544             this.failureType = Roo.form.Action.CLIENT_INVALID;
27545             this.form.afterAction(this, false);
27546         }
27547     },
27548
27549     success : function(response)
27550     {
27551         this.uploadComplete= true;
27552         if (this.haveProgress) {
27553             Roo.MessageBox.hide();
27554         }
27555         
27556         
27557         var result = this.processResponse(response);
27558         if(result === true || result.success){
27559             this.form.afterAction(this, true);
27560             return;
27561         }
27562         if(result.errors){
27563             this.form.markInvalid(result.errors);
27564             this.failureType = Roo.form.Action.SERVER_INVALID;
27565         }
27566         this.form.afterAction(this, false);
27567     },
27568     failure : function(response)
27569     {
27570         this.uploadComplete= true;
27571         if (this.haveProgress) {
27572             Roo.MessageBox.hide();
27573         }
27574         
27575         this.response = response;
27576         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27577         this.form.afterAction(this, false);
27578     },
27579     
27580     handleResponse : function(response){
27581         if(this.form.errorReader){
27582             var rs = this.form.errorReader.read(response);
27583             var errors = [];
27584             if(rs.records){
27585                 for(var i = 0, len = rs.records.length; i < len; i++) {
27586                     var r = rs.records[i];
27587                     errors[i] = r.data;
27588                 }
27589             }
27590             if(errors.length < 1){
27591                 errors = null;
27592             }
27593             return {
27594                 success : rs.success,
27595                 errors : errors
27596             };
27597         }
27598         var ret = false;
27599         try {
27600             ret = Roo.decode(response.responseText);
27601         } catch (e) {
27602             ret = {
27603                 success: false,
27604                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27605                 errors : []
27606             };
27607         }
27608         return ret;
27609         
27610     }
27611 });
27612
27613
27614 Roo.form.Action.Load = function(form, options){
27615     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27616     this.reader = this.form.reader;
27617 };
27618
27619 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27620     type : 'load',
27621
27622     run : function(){
27623         
27624         Roo.Ajax.request(Roo.apply(
27625                 this.createCallback(), {
27626                     method:this.getMethod(),
27627                     url:this.getUrl(false),
27628                     params:this.getParams()
27629         }));
27630     },
27631
27632     success : function(response){
27633         
27634         var result = this.processResponse(response);
27635         if(result === true || !result.success || !result.data){
27636             this.failureType = Roo.form.Action.LOAD_FAILURE;
27637             this.form.afterAction(this, false);
27638             return;
27639         }
27640         this.form.clearInvalid();
27641         this.form.setValues(result.data);
27642         this.form.afterAction(this, true);
27643     },
27644
27645     handleResponse : function(response){
27646         if(this.form.reader){
27647             var rs = this.form.reader.read(response);
27648             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27649             return {
27650                 success : rs.success,
27651                 data : data
27652             };
27653         }
27654         return Roo.decode(response.responseText);
27655     }
27656 });
27657
27658 Roo.form.Action.ACTION_TYPES = {
27659     'load' : Roo.form.Action.Load,
27660     'submit' : Roo.form.Action.Submit
27661 };/*
27662  * Based on:
27663  * Ext JS Library 1.1.1
27664  * Copyright(c) 2006-2007, Ext JS, LLC.
27665  *
27666  * Originally Released Under LGPL - original licence link has changed is not relivant.
27667  *
27668  * Fork - LGPL
27669  * <script type="text/javascript">
27670  */
27671  
27672 /**
27673  * @class Roo.form.Layout
27674  * @extends Roo.Component
27675  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27676  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27677  * @constructor
27678  * @param {Object} config Configuration options
27679  */
27680 Roo.form.Layout = function(config){
27681     var xitems = [];
27682     if (config.items) {
27683         xitems = config.items;
27684         delete config.items;
27685     }
27686     Roo.form.Layout.superclass.constructor.call(this, config);
27687     this.stack = [];
27688     Roo.each(xitems, this.addxtype, this);
27689      
27690 };
27691
27692 Roo.extend(Roo.form.Layout, Roo.Component, {
27693     /**
27694      * @cfg {String/Object} autoCreate
27695      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27696      */
27697     /**
27698      * @cfg {String/Object/Function} style
27699      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27700      * a function which returns such a specification.
27701      */
27702     /**
27703      * @cfg {String} labelAlign
27704      * Valid values are "left," "top" and "right" (defaults to "left")
27705      */
27706     /**
27707      * @cfg {Number} labelWidth
27708      * Fixed width in pixels of all field labels (defaults to undefined)
27709      */
27710     /**
27711      * @cfg {Boolean} clear
27712      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27713      */
27714     clear : true,
27715     /**
27716      * @cfg {String} labelSeparator
27717      * The separator to use after field labels (defaults to ':')
27718      */
27719     labelSeparator : ':',
27720     /**
27721      * @cfg {Boolean} hideLabels
27722      * True to suppress the display of field labels in this layout (defaults to false)
27723      */
27724     hideLabels : false,
27725
27726     // private
27727     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27728     
27729     isLayout : true,
27730     
27731     // private
27732     onRender : function(ct, position){
27733         if(this.el){ // from markup
27734             this.el = Roo.get(this.el);
27735         }else {  // generate
27736             var cfg = this.getAutoCreate();
27737             this.el = ct.createChild(cfg, position);
27738         }
27739         if(this.style){
27740             this.el.applyStyles(this.style);
27741         }
27742         if(this.labelAlign){
27743             this.el.addClass('x-form-label-'+this.labelAlign);
27744         }
27745         if(this.hideLabels){
27746             this.labelStyle = "display:none";
27747             this.elementStyle = "padding-left:0;";
27748         }else{
27749             if(typeof this.labelWidth == 'number'){
27750                 this.labelStyle = "width:"+this.labelWidth+"px;";
27751                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27752             }
27753             if(this.labelAlign == 'top'){
27754                 this.labelStyle = "width:auto;";
27755                 this.elementStyle = "padding-left:0;";
27756             }
27757         }
27758         var stack = this.stack;
27759         var slen = stack.length;
27760         if(slen > 0){
27761             if(!this.fieldTpl){
27762                 var t = new Roo.Template(
27763                     '<div class="x-form-item {5}">',
27764                         '<label for="{0}" style="{2}">{1}{4}</label>',
27765                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27766                         '</div>',
27767                     '</div><div class="x-form-clear-left"></div>'
27768                 );
27769                 t.disableFormats = true;
27770                 t.compile();
27771                 Roo.form.Layout.prototype.fieldTpl = t;
27772             }
27773             for(var i = 0; i < slen; i++) {
27774                 if(stack[i].isFormField){
27775                     this.renderField(stack[i]);
27776                 }else{
27777                     this.renderComponent(stack[i]);
27778                 }
27779             }
27780         }
27781         if(this.clear){
27782             this.el.createChild({cls:'x-form-clear'});
27783         }
27784     },
27785
27786     // private
27787     renderField : function(f){
27788         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27789                f.id, //0
27790                f.fieldLabel, //1
27791                f.labelStyle||this.labelStyle||'', //2
27792                this.elementStyle||'', //3
27793                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27794                f.itemCls||this.itemCls||''  //5
27795        ], true).getPrevSibling());
27796     },
27797
27798     // private
27799     renderComponent : function(c){
27800         c.render(c.isLayout ? this.el : this.el.createChild());    
27801     },
27802     /**
27803      * Adds a object form elements (using the xtype property as the factory method.)
27804      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27805      * @param {Object} config 
27806      */
27807     addxtype : function(o)
27808     {
27809         // create the lement.
27810         o.form = this.form;
27811         var fe = Roo.factory(o, Roo.form);
27812         this.form.allItems.push(fe);
27813         this.stack.push(fe);
27814         
27815         if (fe.isFormField) {
27816             this.form.items.add(fe);
27817         }
27818          
27819         return fe;
27820     }
27821 });
27822
27823 /**
27824  * @class Roo.form.Column
27825  * @extends Roo.form.Layout
27826  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27827  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27828  * @constructor
27829  * @param {Object} config Configuration options
27830  */
27831 Roo.form.Column = function(config){
27832     Roo.form.Column.superclass.constructor.call(this, config);
27833 };
27834
27835 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27836     /**
27837      * @cfg {Number/String} width
27838      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27839      */
27840     /**
27841      * @cfg {String/Object} autoCreate
27842      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27843      */
27844
27845     // private
27846     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27847
27848     // private
27849     onRender : function(ct, position){
27850         Roo.form.Column.superclass.onRender.call(this, ct, position);
27851         if(this.width){
27852             this.el.setWidth(this.width);
27853         }
27854     }
27855 });
27856
27857
27858 /**
27859  * @class Roo.form.Row
27860  * @extends Roo.form.Layout
27861  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27862  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27863  * @constructor
27864  * @param {Object} config Configuration options
27865  */
27866
27867  
27868 Roo.form.Row = function(config){
27869     Roo.form.Row.superclass.constructor.call(this, config);
27870 };
27871  
27872 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27873       /**
27874      * @cfg {Number/String} width
27875      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27876      */
27877     /**
27878      * @cfg {Number/String} height
27879      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27880      */
27881     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27882     
27883     padWidth : 20,
27884     // private
27885     onRender : function(ct, position){
27886         //console.log('row render');
27887         if(!this.rowTpl){
27888             var t = new Roo.Template(
27889                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27890                     '<label for="{0}" style="{2}">{1}{4}</label>',
27891                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27892                     '</div>',
27893                 '</div>'
27894             );
27895             t.disableFormats = true;
27896             t.compile();
27897             Roo.form.Layout.prototype.rowTpl = t;
27898         }
27899         this.fieldTpl = this.rowTpl;
27900         
27901         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27902         var labelWidth = 100;
27903         
27904         if ((this.labelAlign != 'top')) {
27905             if (typeof this.labelWidth == 'number') {
27906                 labelWidth = this.labelWidth
27907             }
27908             this.padWidth =  20 + labelWidth;
27909             
27910         }
27911         
27912         Roo.form.Column.superclass.onRender.call(this, ct, position);
27913         if(this.width){
27914             this.el.setWidth(this.width);
27915         }
27916         if(this.height){
27917             this.el.setHeight(this.height);
27918         }
27919     },
27920     
27921     // private
27922     renderField : function(f){
27923         f.fieldEl = this.fieldTpl.append(this.el, [
27924                f.id, f.fieldLabel,
27925                f.labelStyle||this.labelStyle||'',
27926                this.elementStyle||'',
27927                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27928                f.itemCls||this.itemCls||'',
27929                f.width ? f.width + this.padWidth : 160 + this.padWidth
27930        ],true);
27931     }
27932 });
27933  
27934
27935 /**
27936  * @class Roo.form.FieldSet
27937  * @extends Roo.form.Layout
27938  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
27939  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27940  * @constructor
27941  * @param {Object} config Configuration options
27942  */
27943 Roo.form.FieldSet = function(config){
27944     Roo.form.FieldSet.superclass.constructor.call(this, config);
27945 };
27946
27947 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27948     /**
27949      * @cfg {String} legend
27950      * The text to display as the legend for the FieldSet (defaults to '')
27951      */
27952     /**
27953      * @cfg {String/Object} autoCreate
27954      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27955      */
27956
27957     // private
27958     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27959
27960     // private
27961     onRender : function(ct, position){
27962         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27963         if(this.legend){
27964             this.setLegend(this.legend);
27965         }
27966     },
27967
27968     // private
27969     setLegend : function(text){
27970         if(this.rendered){
27971             this.el.child('legend').update(text);
27972         }
27973     }
27974 });/*
27975  * Based on:
27976  * Ext JS Library 1.1.1
27977  * Copyright(c) 2006-2007, Ext JS, LLC.
27978  *
27979  * Originally Released Under LGPL - original licence link has changed is not relivant.
27980  *
27981  * Fork - LGPL
27982  * <script type="text/javascript">
27983  */
27984 /**
27985  * @class Roo.form.VTypes
27986  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27987  * @static
27988  */
27989 Roo.form.VTypes = function(){
27990     // closure these in so they are only created once.
27991     var alpha = /^[a-zA-Z_]+$/;
27992     var alphanum = /^[a-zA-Z0-9_]+$/;
27993     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
27994     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27995
27996     // All these messages and functions are configurable
27997     return {
27998         /**
27999          * The function used to validate email addresses
28000          * @param {String} value The email address
28001          */
28002         'email' : function(v){
28003             return email.test(v);
28004         },
28005         /**
28006          * The error text to display when the email validation function returns false
28007          * @type String
28008          */
28009         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28010         /**
28011          * The keystroke filter mask to be applied on email input
28012          * @type RegExp
28013          */
28014         'emailMask' : /[a-z0-9_\.\-@]/i,
28015
28016         /**
28017          * The function used to validate URLs
28018          * @param {String} value The URL
28019          */
28020         'url' : function(v){
28021             return url.test(v);
28022         },
28023         /**
28024          * The error text to display when the url validation function returns false
28025          * @type String
28026          */
28027         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28028         
28029         /**
28030          * The function used to validate alpha values
28031          * @param {String} value The value
28032          */
28033         'alpha' : function(v){
28034             return alpha.test(v);
28035         },
28036         /**
28037          * The error text to display when the alpha validation function returns false
28038          * @type String
28039          */
28040         'alphaText' : 'This field should only contain letters and _',
28041         /**
28042          * The keystroke filter mask to be applied on alpha input
28043          * @type RegExp
28044          */
28045         'alphaMask' : /[a-z_]/i,
28046
28047         /**
28048          * The function used to validate alphanumeric values
28049          * @param {String} value The value
28050          */
28051         'alphanum' : function(v){
28052             return alphanum.test(v);
28053         },
28054         /**
28055          * The error text to display when the alphanumeric validation function returns false
28056          * @type String
28057          */
28058         'alphanumText' : 'This field should only contain letters, numbers and _',
28059         /**
28060          * The keystroke filter mask to be applied on alphanumeric input
28061          * @type RegExp
28062          */
28063         'alphanumMask' : /[a-z0-9_]/i
28064     };
28065 }();//<script type="text/javascript">
28066
28067 /**
28068  * @class Roo.form.FCKeditor
28069  * @extends Roo.form.TextArea
28070  * Wrapper around the FCKEditor http://www.fckeditor.net
28071  * @constructor
28072  * Creates a new FCKeditor
28073  * @param {Object} config Configuration options
28074  */
28075 Roo.form.FCKeditor = function(config){
28076     Roo.form.FCKeditor.superclass.constructor.call(this, config);
28077     this.addEvents({
28078          /**
28079          * @event editorinit
28080          * Fired when the editor is initialized - you can add extra handlers here..
28081          * @param {FCKeditor} this
28082          * @param {Object} the FCK object.
28083          */
28084         editorinit : true
28085     });
28086     
28087     
28088 };
28089 Roo.form.FCKeditor.editors = { };
28090 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
28091 {
28092     //defaultAutoCreate : {
28093     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
28094     //},
28095     // private
28096     /**
28097      * @cfg {Object} fck options - see fck manual for details.
28098      */
28099     fckconfig : false,
28100     
28101     /**
28102      * @cfg {Object} fck toolbar set (Basic or Default)
28103      */
28104     toolbarSet : 'Basic',
28105     /**
28106      * @cfg {Object} fck BasePath
28107      */ 
28108     basePath : '/fckeditor/',
28109     
28110     
28111     frame : false,
28112     
28113     value : '',
28114     
28115    
28116     onRender : function(ct, position)
28117     {
28118         if(!this.el){
28119             this.defaultAutoCreate = {
28120                 tag: "textarea",
28121                 style:"width:300px;height:60px;",
28122                 autocomplete: "new-password"
28123             };
28124         }
28125         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
28126         /*
28127         if(this.grow){
28128             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
28129             if(this.preventScrollbars){
28130                 this.el.setStyle("overflow", "hidden");
28131             }
28132             this.el.setHeight(this.growMin);
28133         }
28134         */
28135         //console.log('onrender' + this.getId() );
28136         Roo.form.FCKeditor.editors[this.getId()] = this;
28137          
28138
28139         this.replaceTextarea() ;
28140         
28141     },
28142     
28143     getEditor : function() {
28144         return this.fckEditor;
28145     },
28146     /**
28147      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
28148      * @param {Mixed} value The value to set
28149      */
28150     
28151     
28152     setValue : function(value)
28153     {
28154         //console.log('setValue: ' + value);
28155         
28156         if(typeof(value) == 'undefined') { // not sure why this is happending...
28157             return;
28158         }
28159         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28160         
28161         //if(!this.el || !this.getEditor()) {
28162         //    this.value = value;
28163             //this.setValue.defer(100,this,[value]);    
28164         //    return;
28165         //} 
28166         
28167         if(!this.getEditor()) {
28168             return;
28169         }
28170         
28171         this.getEditor().SetData(value);
28172         
28173         //
28174
28175     },
28176
28177     /**
28178      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
28179      * @return {Mixed} value The field value
28180      */
28181     getValue : function()
28182     {
28183         
28184         if (this.frame && this.frame.dom.style.display == 'none') {
28185             return Roo.form.FCKeditor.superclass.getValue.call(this);
28186         }
28187         
28188         if(!this.el || !this.getEditor()) {
28189            
28190            // this.getValue.defer(100,this); 
28191             return this.value;
28192         }
28193        
28194         
28195         var value=this.getEditor().GetData();
28196         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
28197         return Roo.form.FCKeditor.superclass.getValue.call(this);
28198         
28199
28200     },
28201
28202     /**
28203      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
28204      * @return {Mixed} value The field value
28205      */
28206     getRawValue : function()
28207     {
28208         if (this.frame && this.frame.dom.style.display == 'none') {
28209             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28210         }
28211         
28212         if(!this.el || !this.getEditor()) {
28213             //this.getRawValue.defer(100,this); 
28214             return this.value;
28215             return;
28216         }
28217         
28218         
28219         
28220         var value=this.getEditor().GetData();
28221         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
28222         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
28223          
28224     },
28225     
28226     setSize : function(w,h) {
28227         
28228         
28229         
28230         //if (this.frame && this.frame.dom.style.display == 'none') {
28231         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28232         //    return;
28233         //}
28234         //if(!this.el || !this.getEditor()) {
28235         //    this.setSize.defer(100,this, [w,h]); 
28236         //    return;
28237         //}
28238         
28239         
28240         
28241         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
28242         
28243         this.frame.dom.setAttribute('width', w);
28244         this.frame.dom.setAttribute('height', h);
28245         this.frame.setSize(w,h);
28246         
28247     },
28248     
28249     toggleSourceEdit : function(value) {
28250         
28251       
28252          
28253         this.el.dom.style.display = value ? '' : 'none';
28254         this.frame.dom.style.display = value ?  'none' : '';
28255         
28256     },
28257     
28258     
28259     focus: function(tag)
28260     {
28261         if (this.frame.dom.style.display == 'none') {
28262             return Roo.form.FCKeditor.superclass.focus.call(this);
28263         }
28264         if(!this.el || !this.getEditor()) {
28265             this.focus.defer(100,this, [tag]); 
28266             return;
28267         }
28268         
28269         
28270         
28271         
28272         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
28273         this.getEditor().Focus();
28274         if (tgs.length) {
28275             if (!this.getEditor().Selection.GetSelection()) {
28276                 this.focus.defer(100,this, [tag]); 
28277                 return;
28278             }
28279             
28280             
28281             var r = this.getEditor().EditorDocument.createRange();
28282             r.setStart(tgs[0],0);
28283             r.setEnd(tgs[0],0);
28284             this.getEditor().Selection.GetSelection().removeAllRanges();
28285             this.getEditor().Selection.GetSelection().addRange(r);
28286             this.getEditor().Focus();
28287         }
28288         
28289     },
28290     
28291     
28292     
28293     replaceTextarea : function()
28294     {
28295         if ( document.getElementById( this.getId() + '___Frame' ) ) {
28296             return ;
28297         }
28298         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
28299         //{
28300             // We must check the elements firstly using the Id and then the name.
28301         var oTextarea = document.getElementById( this.getId() );
28302         
28303         var colElementsByName = document.getElementsByName( this.getId() ) ;
28304          
28305         oTextarea.style.display = 'none' ;
28306
28307         if ( oTextarea.tabIndex ) {            
28308             this.TabIndex = oTextarea.tabIndex ;
28309         }
28310         
28311         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
28312         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
28313         this.frame = Roo.get(this.getId() + '___Frame')
28314     },
28315     
28316     _getConfigHtml : function()
28317     {
28318         var sConfig = '' ;
28319
28320         for ( var o in this.fckconfig ) {
28321             sConfig += sConfig.length > 0  ? '&amp;' : '';
28322             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
28323         }
28324
28325         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
28326     },
28327     
28328     
28329     _getIFrameHtml : function()
28330     {
28331         var sFile = 'fckeditor.html' ;
28332         /* no idea what this is about..
28333         try
28334         {
28335             if ( (/fcksource=true/i).test( window.top.location.search ) )
28336                 sFile = 'fckeditor.original.html' ;
28337         }
28338         catch (e) { 
28339         */
28340
28341         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
28342         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
28343         
28344         
28345         var html = '<iframe id="' + this.getId() +
28346             '___Frame" src="' + sLink +
28347             '" width="' + this.width +
28348             '" height="' + this.height + '"' +
28349             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
28350             ' frameborder="0" scrolling="no"></iframe>' ;
28351
28352         return html ;
28353     },
28354     
28355     _insertHtmlBefore : function( html, element )
28356     {
28357         if ( element.insertAdjacentHTML )       {
28358             // IE
28359             element.insertAdjacentHTML( 'beforeBegin', html ) ;
28360         } else { // Gecko
28361             var oRange = document.createRange() ;
28362             oRange.setStartBefore( element ) ;
28363             var oFragment = oRange.createContextualFragment( html );
28364             element.parentNode.insertBefore( oFragment, element ) ;
28365         }
28366     }
28367     
28368     
28369   
28370     
28371     
28372     
28373     
28374
28375 });
28376
28377 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28378
28379 function FCKeditor_OnComplete(editorInstance){
28380     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28381     f.fckEditor = editorInstance;
28382     //console.log("loaded");
28383     f.fireEvent('editorinit', f, editorInstance);
28384
28385   
28386
28387  
28388
28389
28390
28391
28392
28393
28394
28395
28396
28397
28398
28399
28400
28401
28402
28403 //<script type="text/javascript">
28404 /**
28405  * @class Roo.form.GridField
28406  * @extends Roo.form.Field
28407  * Embed a grid (or editable grid into a form)
28408  * STATUS ALPHA
28409  * 
28410  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28411  * it needs 
28412  * xgrid.store = Roo.data.Store
28413  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28414  * xgrid.store.reader = Roo.data.JsonReader 
28415  * 
28416  * 
28417  * @constructor
28418  * Creates a new GridField
28419  * @param {Object} config Configuration options
28420  */
28421 Roo.form.GridField = function(config){
28422     Roo.form.GridField.superclass.constructor.call(this, config);
28423      
28424 };
28425
28426 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28427     /**
28428      * @cfg {Number} width  - used to restrict width of grid..
28429      */
28430     width : 100,
28431     /**
28432      * @cfg {Number} height - used to restrict height of grid..
28433      */
28434     height : 50,
28435      /**
28436      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28437          * 
28438          *}
28439      */
28440     xgrid : false, 
28441     /**
28442      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28443      * {tag: "input", type: "checkbox", autocomplete: "off"})
28444      */
28445    // defaultAutoCreate : { tag: 'div' },
28446     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
28447     /**
28448      * @cfg {String} addTitle Text to include for adding a title.
28449      */
28450     addTitle : false,
28451     //
28452     onResize : function(){
28453         Roo.form.Field.superclass.onResize.apply(this, arguments);
28454     },
28455
28456     initEvents : function(){
28457         // Roo.form.Checkbox.superclass.initEvents.call(this);
28458         // has no events...
28459        
28460     },
28461
28462
28463     getResizeEl : function(){
28464         return this.wrap;
28465     },
28466
28467     getPositionEl : function(){
28468         return this.wrap;
28469     },
28470
28471     // private
28472     onRender : function(ct, position){
28473         
28474         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28475         var style = this.style;
28476         delete this.style;
28477         
28478         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28479         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28480         this.viewEl = this.wrap.createChild({ tag: 'div' });
28481         if (style) {
28482             this.viewEl.applyStyles(style);
28483         }
28484         if (this.width) {
28485             this.viewEl.setWidth(this.width);
28486         }
28487         if (this.height) {
28488             this.viewEl.setHeight(this.height);
28489         }
28490         //if(this.inputValue !== undefined){
28491         //this.setValue(this.value);
28492         
28493         
28494         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28495         
28496         
28497         this.grid.render();
28498         this.grid.getDataSource().on('remove', this.refreshValue, this);
28499         this.grid.getDataSource().on('update', this.refreshValue, this);
28500         this.grid.on('afteredit', this.refreshValue, this);
28501  
28502     },
28503      
28504     
28505     /**
28506      * Sets the value of the item. 
28507      * @param {String} either an object  or a string..
28508      */
28509     setValue : function(v){
28510         //this.value = v;
28511         v = v || []; // empty set..
28512         // this does not seem smart - it really only affects memoryproxy grids..
28513         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28514             var ds = this.grid.getDataSource();
28515             // assumes a json reader..
28516             var data = {}
28517             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28518             ds.loadData( data);
28519         }
28520         // clear selection so it does not get stale.
28521         if (this.grid.sm) { 
28522             this.grid.sm.clearSelections();
28523         }
28524         
28525         Roo.form.GridField.superclass.setValue.call(this, v);
28526         this.refreshValue();
28527         // should load data in the grid really....
28528     },
28529     
28530     // private
28531     refreshValue: function() {
28532          var val = [];
28533         this.grid.getDataSource().each(function(r) {
28534             val.push(r.data);
28535         });
28536         this.el.dom.value = Roo.encode(val);
28537     }
28538     
28539      
28540     
28541     
28542 });/*
28543  * Based on:
28544  * Ext JS Library 1.1.1
28545  * Copyright(c) 2006-2007, Ext JS, LLC.
28546  *
28547  * Originally Released Under LGPL - original licence link has changed is not relivant.
28548  *
28549  * Fork - LGPL
28550  * <script type="text/javascript">
28551  */
28552 /**
28553  * @class Roo.form.DisplayField
28554  * @extends Roo.form.Field
28555  * A generic Field to display non-editable data.
28556  * @cfg {Boolean} closable (true|false) default false
28557  * @constructor
28558  * Creates a new Display Field item.
28559  * @param {Object} config Configuration options
28560  */
28561 Roo.form.DisplayField = function(config){
28562     Roo.form.DisplayField.superclass.constructor.call(this, config);
28563     
28564     this.addEvents({
28565         /**
28566          * @event close
28567          * Fires after the click the close btn
28568              * @param {Roo.form.DisplayField} this
28569              */
28570         close : true
28571     });
28572 };
28573
28574 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28575     inputType:      'hidden',
28576     allowBlank:     true,
28577     readOnly:         true,
28578     
28579  
28580     /**
28581      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28582      */
28583     focusClass : undefined,
28584     /**
28585      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28586      */
28587     fieldClass: 'x-form-field',
28588     
28589      /**
28590      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28591      */
28592     valueRenderer: undefined,
28593     
28594     width: 100,
28595     /**
28596      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28597      * {tag: "input", type: "checkbox", autocomplete: "off"})
28598      */
28599      
28600  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28601  
28602     closable : false,
28603     
28604     onResize : function(){
28605         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28606         
28607     },
28608
28609     initEvents : function(){
28610         // Roo.form.Checkbox.superclass.initEvents.call(this);
28611         // has no events...
28612         
28613         if(this.closable){
28614             this.closeEl.on('click', this.onClose, this);
28615         }
28616        
28617     },
28618
28619
28620     getResizeEl : function(){
28621         return this.wrap;
28622     },
28623
28624     getPositionEl : function(){
28625         return this.wrap;
28626     },
28627
28628     // private
28629     onRender : function(ct, position){
28630         
28631         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28632         //if(this.inputValue !== undefined){
28633         this.wrap = this.el.wrap();
28634         
28635         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28636         
28637         if(this.closable){
28638             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
28639         }
28640         
28641         if (this.bodyStyle) {
28642             this.viewEl.applyStyles(this.bodyStyle);
28643         }
28644         //this.viewEl.setStyle('padding', '2px');
28645         
28646         this.setValue(this.value);
28647         
28648     },
28649 /*
28650     // private
28651     initValue : Roo.emptyFn,
28652
28653   */
28654
28655         // private
28656     onClick : function(){
28657         
28658     },
28659
28660     /**
28661      * Sets the checked state of the checkbox.
28662      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28663      */
28664     setValue : function(v){
28665         this.value = v;
28666         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28667         // this might be called before we have a dom element..
28668         if (!this.viewEl) {
28669             return;
28670         }
28671         this.viewEl.dom.innerHTML = html;
28672         Roo.form.DisplayField.superclass.setValue.call(this, v);
28673
28674     },
28675     
28676     onClose : function(e)
28677     {
28678         e.preventDefault();
28679         
28680         this.fireEvent('close', this);
28681     }
28682 });/*
28683  * 
28684  * Licence- LGPL
28685  * 
28686  */
28687
28688 /**
28689  * @class Roo.form.DayPicker
28690  * @extends Roo.form.Field
28691  * A Day picker show [M] [T] [W] ....
28692  * @constructor
28693  * Creates a new Day Picker
28694  * @param {Object} config Configuration options
28695  */
28696 Roo.form.DayPicker= function(config){
28697     Roo.form.DayPicker.superclass.constructor.call(this, config);
28698      
28699 };
28700
28701 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28702     /**
28703      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28704      */
28705     focusClass : undefined,
28706     /**
28707      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28708      */
28709     fieldClass: "x-form-field",
28710    
28711     /**
28712      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28713      * {tag: "input", type: "checkbox", autocomplete: "off"})
28714      */
28715     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
28716     
28717    
28718     actionMode : 'viewEl', 
28719     //
28720     // private
28721  
28722     inputType : 'hidden',
28723     
28724      
28725     inputElement: false, // real input element?
28726     basedOn: false, // ????
28727     
28728     isFormField: true, // not sure where this is needed!!!!
28729
28730     onResize : function(){
28731         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28732         if(!this.boxLabel){
28733             this.el.alignTo(this.wrap, 'c-c');
28734         }
28735     },
28736
28737     initEvents : function(){
28738         Roo.form.Checkbox.superclass.initEvents.call(this);
28739         this.el.on("click", this.onClick,  this);
28740         this.el.on("change", this.onClick,  this);
28741     },
28742
28743
28744     getResizeEl : function(){
28745         return this.wrap;
28746     },
28747
28748     getPositionEl : function(){
28749         return this.wrap;
28750     },
28751
28752     
28753     // private
28754     onRender : function(ct, position){
28755         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28756        
28757         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28758         
28759         var r1 = '<table><tr>';
28760         var r2 = '<tr class="x-form-daypick-icons">';
28761         for (var i=0; i < 7; i++) {
28762             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28763             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28764         }
28765         
28766         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28767         viewEl.select('img').on('click', this.onClick, this);
28768         this.viewEl = viewEl;   
28769         
28770         
28771         // this will not work on Chrome!!!
28772         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28773         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28774         
28775         
28776           
28777
28778     },
28779
28780     // private
28781     initValue : Roo.emptyFn,
28782
28783     /**
28784      * Returns the checked state of the checkbox.
28785      * @return {Boolean} True if checked, else false
28786      */
28787     getValue : function(){
28788         return this.el.dom.value;
28789         
28790     },
28791
28792         // private
28793     onClick : function(e){ 
28794         //this.setChecked(!this.checked);
28795         Roo.get(e.target).toggleClass('x-menu-item-checked');
28796         this.refreshValue();
28797         //if(this.el.dom.checked != this.checked){
28798         //    this.setValue(this.el.dom.checked);
28799        // }
28800     },
28801     
28802     // private
28803     refreshValue : function()
28804     {
28805         var val = '';
28806         this.viewEl.select('img',true).each(function(e,i,n)  {
28807             val += e.is(".x-menu-item-checked") ? String(n) : '';
28808         });
28809         this.setValue(val, true);
28810     },
28811
28812     /**
28813      * Sets the checked state of the checkbox.
28814      * On is always based on a string comparison between inputValue and the param.
28815      * @param {Boolean/String} value - the value to set 
28816      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28817      */
28818     setValue : function(v,suppressEvent){
28819         if (!this.el.dom) {
28820             return;
28821         }
28822         var old = this.el.dom.value ;
28823         this.el.dom.value = v;
28824         if (suppressEvent) {
28825             return ;
28826         }
28827          
28828         // update display..
28829         this.viewEl.select('img',true).each(function(e,i,n)  {
28830             
28831             var on = e.is(".x-menu-item-checked");
28832             var newv = v.indexOf(String(n)) > -1;
28833             if (on != newv) {
28834                 e.toggleClass('x-menu-item-checked');
28835             }
28836             
28837         });
28838         
28839         
28840         this.fireEvent('change', this, v, old);
28841         
28842         
28843     },
28844    
28845     // handle setting of hidden value by some other method!!?!?
28846     setFromHidden: function()
28847     {
28848         if(!this.el){
28849             return;
28850         }
28851         //console.log("SET FROM HIDDEN");
28852         //alert('setFrom hidden');
28853         this.setValue(this.el.dom.value);
28854     },
28855     
28856     onDestroy : function()
28857     {
28858         if(this.viewEl){
28859             Roo.get(this.viewEl).remove();
28860         }
28861          
28862         Roo.form.DayPicker.superclass.onDestroy.call(this);
28863     }
28864
28865 });/*
28866  * RooJS Library 1.1.1
28867  * Copyright(c) 2008-2011  Alan Knowles
28868  *
28869  * License - LGPL
28870  */
28871  
28872
28873 /**
28874  * @class Roo.form.ComboCheck
28875  * @extends Roo.form.ComboBox
28876  * A combobox for multiple select items.
28877  *
28878  * FIXME - could do with a reset button..
28879  * 
28880  * @constructor
28881  * Create a new ComboCheck
28882  * @param {Object} config Configuration options
28883  */
28884 Roo.form.ComboCheck = function(config){
28885     Roo.form.ComboCheck.superclass.constructor.call(this, config);
28886     // should verify some data...
28887     // like
28888     // hiddenName = required..
28889     // displayField = required
28890     // valudField == required
28891     var req= [ 'hiddenName', 'displayField', 'valueField' ];
28892     var _t = this;
28893     Roo.each(req, function(e) {
28894         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
28895             throw "Roo.form.ComboCheck : missing value for: " + e;
28896         }
28897     });
28898     
28899     
28900 };
28901
28902 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
28903      
28904      
28905     editable : false,
28906      
28907     selectedClass: 'x-menu-item-checked', 
28908     
28909     // private
28910     onRender : function(ct, position){
28911         var _t = this;
28912         
28913         
28914         
28915         if(!this.tpl){
28916             var cls = 'x-combo-list';
28917
28918             
28919             this.tpl =  new Roo.Template({
28920                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
28921                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
28922                    '<span>{' + this.displayField + '}</span>' +
28923                     '</div>' 
28924                 
28925             });
28926         }
28927  
28928         
28929         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
28930         this.view.singleSelect = false;
28931         this.view.multiSelect = true;
28932         this.view.toggleSelect = true;
28933         this.pageTb.add(new Roo.Toolbar.Fill(), {
28934             
28935             text: 'Done',
28936             handler: function()
28937             {
28938                 _t.collapse();
28939             }
28940         });
28941     },
28942     
28943     onViewOver : function(e, t){
28944         // do nothing...
28945         return;
28946         
28947     },
28948     
28949     onViewClick : function(doFocus,index){
28950         return;
28951         
28952     },
28953     select: function () {
28954         //Roo.log("SELECT CALLED");
28955     },
28956      
28957     selectByValue : function(xv, scrollIntoView){
28958         var ar = this.getValueArray();
28959         var sels = [];
28960         
28961         Roo.each(ar, function(v) {
28962             if(v === undefined || v === null){
28963                 return;
28964             }
28965             var r = this.findRecord(this.valueField, v);
28966             if(r){
28967                 sels.push(this.store.indexOf(r))
28968                 
28969             }
28970         },this);
28971         this.view.select(sels);
28972         return false;
28973     },
28974     
28975     
28976     
28977     onSelect : function(record, index){
28978        // Roo.log("onselect Called");
28979        // this is only called by the clear button now..
28980         this.view.clearSelections();
28981         this.setValue('[]');
28982         if (this.value != this.valueBefore) {
28983             this.fireEvent('change', this, this.value, this.valueBefore);
28984             this.valueBefore = this.value;
28985         }
28986     },
28987     getValueArray : function()
28988     {
28989         var ar = [] ;
28990         
28991         try {
28992             //Roo.log(this.value);
28993             if (typeof(this.value) == 'undefined') {
28994                 return [];
28995             }
28996             var ar = Roo.decode(this.value);
28997             return  ar instanceof Array ? ar : []; //?? valid?
28998             
28999         } catch(e) {
29000             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29001             return [];
29002         }
29003          
29004     },
29005     expand : function ()
29006     {
29007         
29008         Roo.form.ComboCheck.superclass.expand.call(this);
29009         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
29010         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
29011         
29012
29013     },
29014     
29015     collapse : function(){
29016         Roo.form.ComboCheck.superclass.collapse.call(this);
29017         var sl = this.view.getSelectedIndexes();
29018         var st = this.store;
29019         var nv = [];
29020         var tv = [];
29021         var r;
29022         Roo.each(sl, function(i) {
29023             r = st.getAt(i);
29024             nv.push(r.get(this.valueField));
29025         },this);
29026         this.setValue(Roo.encode(nv));
29027         if (this.value != this.valueBefore) {
29028
29029             this.fireEvent('change', this, this.value, this.valueBefore);
29030             this.valueBefore = this.value;
29031         }
29032         
29033     },
29034     
29035     setValue : function(v){
29036         // Roo.log(v);
29037         this.value = v;
29038         
29039         var vals = this.getValueArray();
29040         var tv = [];
29041         Roo.each(vals, function(k) {
29042             var r = this.findRecord(this.valueField, k);
29043             if(r){
29044                 tv.push(r.data[this.displayField]);
29045             }else if(this.valueNotFoundText !== undefined){
29046                 tv.push( this.valueNotFoundText );
29047             }
29048         },this);
29049        // Roo.log(tv);
29050         
29051         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29052         this.hiddenField.value = v;
29053         this.value = v;
29054     }
29055     
29056 });/*
29057  * Based on:
29058  * Ext JS Library 1.1.1
29059  * Copyright(c) 2006-2007, Ext JS, LLC.
29060  *
29061  * Originally Released Under LGPL - original licence link has changed is not relivant.
29062  *
29063  * Fork - LGPL
29064  * <script type="text/javascript">
29065  */
29066  
29067 /**
29068  * @class Roo.form.Signature
29069  * @extends Roo.form.Field
29070  * Signature field.  
29071  * @constructor
29072  * 
29073  * @param {Object} config Configuration options
29074  */
29075
29076 Roo.form.Signature = function(config){
29077     Roo.form.Signature.superclass.constructor.call(this, config);
29078     
29079     this.addEvents({// not in used??
29080          /**
29081          * @event confirm
29082          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
29083              * @param {Roo.form.Signature} combo This combo box
29084              */
29085         'confirm' : true,
29086         /**
29087          * @event reset
29088          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
29089              * @param {Roo.form.ComboBox} combo This combo box
29090              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
29091              */
29092         'reset' : true
29093     });
29094 };
29095
29096 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
29097     /**
29098      * @cfg {Object} labels Label to use when rendering a form.
29099      * defaults to 
29100      * labels : { 
29101      *      clear : "Clear",
29102      *      confirm : "Confirm"
29103      *  }
29104      */
29105     labels : { 
29106         clear : "Clear",
29107         confirm : "Confirm"
29108     },
29109     /**
29110      * @cfg {Number} width The signature panel width (defaults to 300)
29111      */
29112     width: 300,
29113     /**
29114      * @cfg {Number} height The signature panel height (defaults to 100)
29115      */
29116     height : 100,
29117     /**
29118      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
29119      */
29120     allowBlank : false,
29121     
29122     //private
29123     // {Object} signPanel The signature SVG panel element (defaults to {})
29124     signPanel : {},
29125     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
29126     isMouseDown : false,
29127     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
29128     isConfirmed : false,
29129     // {String} signatureTmp SVG mapping string (defaults to empty string)
29130     signatureTmp : '',
29131     
29132     
29133     defaultAutoCreate : { // modified by initCompnoent..
29134         tag: "input",
29135         type:"hidden"
29136     },
29137
29138     // private
29139     onRender : function(ct, position){
29140         
29141         Roo.form.Signature.superclass.onRender.call(this, ct, position);
29142         
29143         this.wrap = this.el.wrap({
29144             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
29145         });
29146         
29147         this.createToolbar(this);
29148         this.signPanel = this.wrap.createChild({
29149                 tag: 'div',
29150                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
29151             }, this.el
29152         );
29153             
29154         this.svgID = Roo.id();
29155         this.svgEl = this.signPanel.createChild({
29156               xmlns : 'http://www.w3.org/2000/svg',
29157               tag : 'svg',
29158               id : this.svgID + "-svg",
29159               width: this.width,
29160               height: this.height,
29161               viewBox: '0 0 '+this.width+' '+this.height,
29162               cn : [
29163                 {
29164                     tag: "rect",
29165                     id: this.svgID + "-svg-r",
29166                     width: this.width,
29167                     height: this.height,
29168                     fill: "#ffa"
29169                 },
29170                 {
29171                     tag: "line",
29172                     id: this.svgID + "-svg-l",
29173                     x1: "0", // start
29174                     y1: (this.height*0.8), // start set the line in 80% of height
29175                     x2: this.width, // end
29176                     y2: (this.height*0.8), // end set the line in 80% of height
29177                     'stroke': "#666",
29178                     'stroke-width': "1",
29179                     'stroke-dasharray': "3",
29180                     'shape-rendering': "crispEdges",
29181                     'pointer-events': "none"
29182                 },
29183                 {
29184                     tag: "path",
29185                     id: this.svgID + "-svg-p",
29186                     'stroke': "navy",
29187                     'stroke-width': "3",
29188                     'fill': "none",
29189                     'pointer-events': 'none'
29190                 }
29191               ]
29192         });
29193         this.createSVG();
29194         this.svgBox = this.svgEl.dom.getScreenCTM();
29195     },
29196     createSVG : function(){ 
29197         var svg = this.signPanel;
29198         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
29199         var t = this;
29200
29201         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
29202         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
29203         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
29204         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
29205         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
29206         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
29207         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
29208         
29209     },
29210     isTouchEvent : function(e){
29211         return e.type.match(/^touch/);
29212     },
29213     getCoords : function (e) {
29214         var pt    = this.svgEl.dom.createSVGPoint();
29215         pt.x = e.clientX; 
29216         pt.y = e.clientY;
29217         if (this.isTouchEvent(e)) {
29218             pt.x =  e.targetTouches[0].clientX;
29219             pt.y = e.targetTouches[0].clientY;
29220         }
29221         var a = this.svgEl.dom.getScreenCTM();
29222         var b = a.inverse();
29223         var mx = pt.matrixTransform(b);
29224         return mx.x + ',' + mx.y;
29225     },
29226     //mouse event headler 
29227     down : function (e) {
29228         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
29229         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
29230         
29231         this.isMouseDown = true;
29232         
29233         e.preventDefault();
29234     },
29235     move : function (e) {
29236         if (this.isMouseDown) {
29237             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
29238             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
29239         }
29240         
29241         e.preventDefault();
29242     },
29243     up : function (e) {
29244         this.isMouseDown = false;
29245         var sp = this.signatureTmp.split(' ');
29246         
29247         if(sp.length > 1){
29248             if(!sp[sp.length-2].match(/^L/)){
29249                 sp.pop();
29250                 sp.pop();
29251                 sp.push("");
29252                 this.signatureTmp = sp.join(" ");
29253             }
29254         }
29255         if(this.getValue() != this.signatureTmp){
29256             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29257             this.isConfirmed = false;
29258         }
29259         e.preventDefault();
29260     },
29261     
29262     /**
29263      * Protected method that will not generally be called directly. It
29264      * is called when the editor creates its toolbar. Override this method if you need to
29265      * add custom toolbar buttons.
29266      * @param {HtmlEditor} editor
29267      */
29268     createToolbar : function(editor){
29269          function btn(id, toggle, handler){
29270             var xid = fid + '-'+ id ;
29271             return {
29272                 id : xid,
29273                 cmd : id,
29274                 cls : 'x-btn-icon x-edit-'+id,
29275                 enableToggle:toggle !== false,
29276                 scope: editor, // was editor...
29277                 handler:handler||editor.relayBtnCmd,
29278                 clickEvent:'mousedown',
29279                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
29280                 tabIndex:-1
29281             };
29282         }
29283         
29284         
29285         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
29286         this.tb = tb;
29287         this.tb.add(
29288            {
29289                 cls : ' x-signature-btn x-signature-'+id,
29290                 scope: editor, // was editor...
29291                 handler: this.reset,
29292                 clickEvent:'mousedown',
29293                 text: this.labels.clear
29294             },
29295             {
29296                  xtype : 'Fill',
29297                  xns: Roo.Toolbar
29298             }, 
29299             {
29300                 cls : '  x-signature-btn x-signature-'+id,
29301                 scope: editor, // was editor...
29302                 handler: this.confirmHandler,
29303                 clickEvent:'mousedown',
29304                 text: this.labels.confirm
29305             }
29306         );
29307     
29308     },
29309     //public
29310     /**
29311      * when user is clicked confirm then show this image.....
29312      * 
29313      * @return {String} Image Data URI
29314      */
29315     getImageDataURI : function(){
29316         var svg = this.svgEl.dom.parentNode.innerHTML;
29317         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
29318         return src; 
29319     },
29320     /**
29321      * 
29322      * @return {Boolean} this.isConfirmed
29323      */
29324     getConfirmed : function(){
29325         return this.isConfirmed;
29326     },
29327     /**
29328      * 
29329      * @return {Number} this.width
29330      */
29331     getWidth : function(){
29332         return this.width;
29333     },
29334     /**
29335      * 
29336      * @return {Number} this.height
29337      */
29338     getHeight : function(){
29339         return this.height;
29340     },
29341     // private
29342     getSignature : function(){
29343         return this.signatureTmp;
29344     },
29345     // private
29346     reset : function(){
29347         this.signatureTmp = '';
29348         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29349         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
29350         this.isConfirmed = false;
29351         Roo.form.Signature.superclass.reset.call(this);
29352     },
29353     setSignature : function(s){
29354         this.signatureTmp = s;
29355         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
29356         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
29357         this.setValue(s);
29358         this.isConfirmed = false;
29359         Roo.form.Signature.superclass.reset.call(this);
29360     }, 
29361     test : function(){
29362 //        Roo.log(this.signPanel.dom.contentWindow.up())
29363     },
29364     //private
29365     setConfirmed : function(){
29366         
29367         
29368         
29369 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
29370     },
29371     // private
29372     confirmHandler : function(){
29373         if(!this.getSignature()){
29374             return;
29375         }
29376         
29377         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
29378         this.setValue(this.getSignature());
29379         this.isConfirmed = true;
29380         
29381         this.fireEvent('confirm', this);
29382     },
29383     // private
29384     // Subclasses should provide the validation implementation by overriding this
29385     validateValue : function(value){
29386         if(this.allowBlank){
29387             return true;
29388         }
29389         
29390         if(this.isConfirmed){
29391             return true;
29392         }
29393         return false;
29394     }
29395 });/*
29396  * Based on:
29397  * Ext JS Library 1.1.1
29398  * Copyright(c) 2006-2007, Ext JS, LLC.
29399  *
29400  * Originally Released Under LGPL - original licence link has changed is not relivant.
29401  *
29402  * Fork - LGPL
29403  * <script type="text/javascript">
29404  */
29405  
29406
29407 /**
29408  * @class Roo.form.ComboBox
29409  * @extends Roo.form.TriggerField
29410  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
29411  * @constructor
29412  * Create a new ComboBox.
29413  * @param {Object} config Configuration options
29414  */
29415 Roo.form.Select = function(config){
29416     Roo.form.Select.superclass.constructor.call(this, config);
29417      
29418 };
29419
29420 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
29421     /**
29422      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
29423      */
29424     /**
29425      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
29426      * rendering into an Roo.Editor, defaults to false)
29427      */
29428     /**
29429      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
29430      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
29431      */
29432     /**
29433      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
29434      */
29435     /**
29436      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
29437      * the dropdown list (defaults to undefined, with no header element)
29438      */
29439
29440      /**
29441      * @cfg {String/Roo.Template} tpl The template to use to render the output
29442      */
29443      
29444     // private
29445     defaultAutoCreate : {tag: "select"  },
29446     /**
29447      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
29448      */
29449     listWidth: undefined,
29450     /**
29451      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
29452      * mode = 'remote' or 'text' if mode = 'local')
29453      */
29454     displayField: undefined,
29455     /**
29456      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
29457      * mode = 'remote' or 'value' if mode = 'local'). 
29458      * Note: use of a valueField requires the user make a selection
29459      * in order for a value to be mapped.
29460      */
29461     valueField: undefined,
29462     
29463     
29464     /**
29465      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
29466      * field's data value (defaults to the underlying DOM element's name)
29467      */
29468     hiddenName: undefined,
29469     /**
29470      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
29471      */
29472     listClass: '',
29473     /**
29474      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
29475      */
29476     selectedClass: 'x-combo-selected',
29477     /**
29478      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
29479      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
29480      * which displays a downward arrow icon).
29481      */
29482     triggerClass : 'x-form-arrow-trigger',
29483     /**
29484      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29485      */
29486     shadow:'sides',
29487     /**
29488      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
29489      * anchor positions (defaults to 'tl-bl')
29490      */
29491     listAlign: 'tl-bl?',
29492     /**
29493      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
29494      */
29495     maxHeight: 300,
29496     /**
29497      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
29498      * query specified by the allQuery config option (defaults to 'query')
29499      */
29500     triggerAction: 'query',
29501     /**
29502      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
29503      * (defaults to 4, does not apply if editable = false)
29504      */
29505     minChars : 4,
29506     /**
29507      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
29508      * delay (typeAheadDelay) if it matches a known value (defaults to false)
29509      */
29510     typeAhead: false,
29511     /**
29512      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
29513      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
29514      */
29515     queryDelay: 500,
29516     /**
29517      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
29518      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
29519      */
29520     pageSize: 0,
29521     /**
29522      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
29523      * when editable = true (defaults to false)
29524      */
29525     selectOnFocus:false,
29526     /**
29527      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
29528      */
29529     queryParam: 'query',
29530     /**
29531      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
29532      * when mode = 'remote' (defaults to 'Loading...')
29533      */
29534     loadingText: 'Loading...',
29535     /**
29536      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
29537      */
29538     resizable: false,
29539     /**
29540      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
29541      */
29542     handleHeight : 8,
29543     /**
29544      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
29545      * traditional select (defaults to true)
29546      */
29547     editable: true,
29548     /**
29549      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
29550      */
29551     allQuery: '',
29552     /**
29553      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
29554      */
29555     mode: 'remote',
29556     /**
29557      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
29558      * listWidth has a higher value)
29559      */
29560     minListWidth : 70,
29561     /**
29562      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
29563      * allow the user to set arbitrary text into the field (defaults to false)
29564      */
29565     forceSelection:false,
29566     /**
29567      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
29568      * if typeAhead = true (defaults to 250)
29569      */
29570     typeAheadDelay : 250,
29571     /**
29572      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
29573      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
29574      */
29575     valueNotFoundText : undefined,
29576     
29577     /**
29578      * @cfg {String} defaultValue The value displayed after loading the store.
29579      */
29580     defaultValue: '',
29581     
29582     /**
29583      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
29584      */
29585     blockFocus : false,
29586     
29587     /**
29588      * @cfg {Boolean} disableClear Disable showing of clear button.
29589      */
29590     disableClear : false,
29591     /**
29592      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
29593      */
29594     alwaysQuery : false,
29595     
29596     //private
29597     addicon : false,
29598     editicon: false,
29599     
29600     // element that contains real text value.. (when hidden is used..)
29601      
29602     // private
29603     onRender : function(ct, position){
29604         Roo.form.Field.prototype.onRender.call(this, ct, position);
29605         
29606         if(this.store){
29607             this.store.on('beforeload', this.onBeforeLoad, this);
29608             this.store.on('load', this.onLoad, this);
29609             this.store.on('loadexception', this.onLoadException, this);
29610             this.store.load({});
29611         }
29612         
29613         
29614         
29615     },
29616
29617     // private
29618     initEvents : function(){
29619         //Roo.form.ComboBox.superclass.initEvents.call(this);
29620  
29621     },
29622
29623     onDestroy : function(){
29624        
29625         if(this.store){
29626             this.store.un('beforeload', this.onBeforeLoad, this);
29627             this.store.un('load', this.onLoad, this);
29628             this.store.un('loadexception', this.onLoadException, this);
29629         }
29630         //Roo.form.ComboBox.superclass.onDestroy.call(this);
29631     },
29632
29633     // private
29634     fireKey : function(e){
29635         if(e.isNavKeyPress() && !this.list.isVisible()){
29636             this.fireEvent("specialkey", this, e);
29637         }
29638     },
29639
29640     // private
29641     onResize: function(w, h){
29642         
29643         return; 
29644     
29645         
29646     },
29647
29648     /**
29649      * Allow or prevent the user from directly editing the field text.  If false is passed,
29650      * the user will only be able to select from the items defined in the dropdown list.  This method
29651      * is the runtime equivalent of setting the 'editable' config option at config time.
29652      * @param {Boolean} value True to allow the user to directly edit the field text
29653      */
29654     setEditable : function(value){
29655          
29656     },
29657
29658     // private
29659     onBeforeLoad : function(){
29660         
29661         Roo.log("Select before load");
29662         return;
29663     
29664         this.innerList.update(this.loadingText ?
29665                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
29666         //this.restrictHeight();
29667         this.selectedIndex = -1;
29668     },
29669
29670     // private
29671     onLoad : function(){
29672
29673     
29674         var dom = this.el.dom;
29675         dom.innerHTML = '';
29676          var od = dom.ownerDocument;
29677          
29678         if (this.emptyText) {
29679             var op = od.createElement('option');
29680             op.setAttribute('value', '');
29681             op.innerHTML = String.format('{0}', this.emptyText);
29682             dom.appendChild(op);
29683         }
29684         if(this.store.getCount() > 0){
29685            
29686             var vf = this.valueField;
29687             var df = this.displayField;
29688             this.store.data.each(function(r) {
29689                 // which colmsn to use... testing - cdoe / title..
29690                 var op = od.createElement('option');
29691                 op.setAttribute('value', r.data[vf]);
29692                 op.innerHTML = String.format('{0}', r.data[df]);
29693                 dom.appendChild(op);
29694             });
29695             if (typeof(this.defaultValue != 'undefined')) {
29696                 this.setValue(this.defaultValue);
29697             }
29698             
29699              
29700         }else{
29701             //this.onEmptyResults();
29702         }
29703         //this.el.focus();
29704     },
29705     // private
29706     onLoadException : function()
29707     {
29708         dom.innerHTML = '';
29709             
29710         Roo.log("Select on load exception");
29711         return;
29712     
29713         this.collapse();
29714         Roo.log(this.store.reader.jsonData);
29715         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
29716             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
29717         }
29718         
29719         
29720     },
29721     // private
29722     onTypeAhead : function(){
29723          
29724     },
29725
29726     // private
29727     onSelect : function(record, index){
29728         Roo.log('on select?');
29729         return;
29730         if(this.fireEvent('beforeselect', this, record, index) !== false){
29731             this.setFromData(index > -1 ? record.data : false);
29732             this.collapse();
29733             this.fireEvent('select', this, record, index);
29734         }
29735     },
29736
29737     /**
29738      * Returns the currently selected field value or empty string if no value is set.
29739      * @return {String} value The selected value
29740      */
29741     getValue : function(){
29742         var dom = this.el.dom;
29743         this.value = dom.options[dom.selectedIndex].value;
29744         return this.value;
29745         
29746     },
29747
29748     /**
29749      * Clears any text/value currently set in the field
29750      */
29751     clearValue : function(){
29752         this.value = '';
29753         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
29754         
29755     },
29756
29757     /**
29758      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
29759      * will be displayed in the field.  If the value does not match the data value of an existing item,
29760      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
29761      * Otherwise the field will be blank (although the value will still be set).
29762      * @param {String} value The value to match
29763      */
29764     setValue : function(v){
29765         var d = this.el.dom;
29766         for (var i =0; i < d.options.length;i++) {
29767             if (v == d.options[i].value) {
29768                 d.selectedIndex = i;
29769                 this.value = v;
29770                 return;
29771             }
29772         }
29773         this.clearValue();
29774     },
29775     /**
29776      * @property {Object} the last set data for the element
29777      */
29778     
29779     lastData : false,
29780     /**
29781      * Sets the value of the field based on a object which is related to the record format for the store.
29782      * @param {Object} value the value to set as. or false on reset?
29783      */
29784     setFromData : function(o){
29785         Roo.log('setfrom data?');
29786          
29787         
29788         
29789     },
29790     // private
29791     reset : function(){
29792         this.clearValue();
29793     },
29794     // private
29795     findRecord : function(prop, value){
29796         
29797         return false;
29798     
29799         var record;
29800         if(this.store.getCount() > 0){
29801             this.store.each(function(r){
29802                 if(r.data[prop] == value){
29803                     record = r;
29804                     return false;
29805                 }
29806                 return true;
29807             });
29808         }
29809         return record;
29810     },
29811     
29812     getName: function()
29813     {
29814         // returns hidden if it's set..
29815         if (!this.rendered) {return ''};
29816         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
29817         
29818     },
29819      
29820
29821     
29822
29823     // private
29824     onEmptyResults : function(){
29825         Roo.log('empty results');
29826         //this.collapse();
29827     },
29828
29829     /**
29830      * Returns true if the dropdown list is expanded, else false.
29831      */
29832     isExpanded : function(){
29833         return false;
29834     },
29835
29836     /**
29837      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
29838      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29839      * @param {String} value The data value of the item to select
29840      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29841      * selected item if it is not currently in view (defaults to true)
29842      * @return {Boolean} True if the value matched an item in the list, else false
29843      */
29844     selectByValue : function(v, scrollIntoView){
29845         Roo.log('select By Value');
29846         return false;
29847     
29848         if(v !== undefined && v !== null){
29849             var r = this.findRecord(this.valueField || this.displayField, v);
29850             if(r){
29851                 this.select(this.store.indexOf(r), scrollIntoView);
29852                 return true;
29853             }
29854         }
29855         return false;
29856     },
29857
29858     /**
29859      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
29860      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
29861      * @param {Number} index The zero-based index of the list item to select
29862      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
29863      * selected item if it is not currently in view (defaults to true)
29864      */
29865     select : function(index, scrollIntoView){
29866         Roo.log('select ');
29867         return  ;
29868         
29869         this.selectedIndex = index;
29870         this.view.select(index);
29871         if(scrollIntoView !== false){
29872             var el = this.view.getNode(index);
29873             if(el){
29874                 this.innerList.scrollChildIntoView(el, false);
29875             }
29876         }
29877     },
29878
29879       
29880
29881     // private
29882     validateBlur : function(){
29883         
29884         return;
29885         
29886     },
29887
29888     // private
29889     initQuery : function(){
29890         this.doQuery(this.getRawValue());
29891     },
29892
29893     // private
29894     doForce : function(){
29895         if(this.el.dom.value.length > 0){
29896             this.el.dom.value =
29897                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
29898              
29899         }
29900     },
29901
29902     /**
29903      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
29904      * query allowing the query action to be canceled if needed.
29905      * @param {String} query The SQL query to execute
29906      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
29907      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
29908      * saved in the current store (defaults to false)
29909      */
29910     doQuery : function(q, forceAll){
29911         
29912         Roo.log('doQuery?');
29913         if(q === undefined || q === null){
29914             q = '';
29915         }
29916         var qe = {
29917             query: q,
29918             forceAll: forceAll,
29919             combo: this,
29920             cancel:false
29921         };
29922         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
29923             return false;
29924         }
29925         q = qe.query;
29926         forceAll = qe.forceAll;
29927         if(forceAll === true || (q.length >= this.minChars)){
29928             if(this.lastQuery != q || this.alwaysQuery){
29929                 this.lastQuery = q;
29930                 if(this.mode == 'local'){
29931                     this.selectedIndex = -1;
29932                     if(forceAll){
29933                         this.store.clearFilter();
29934                     }else{
29935                         this.store.filter(this.displayField, q);
29936                     }
29937                     this.onLoad();
29938                 }else{
29939                     this.store.baseParams[this.queryParam] = q;
29940                     this.store.load({
29941                         params: this.getParams(q)
29942                     });
29943                     this.expand();
29944                 }
29945             }else{
29946                 this.selectedIndex = -1;
29947                 this.onLoad();   
29948             }
29949         }
29950     },
29951
29952     // private
29953     getParams : function(q){
29954         var p = {};
29955         //p[this.queryParam] = q;
29956         if(this.pageSize){
29957             p.start = 0;
29958             p.limit = this.pageSize;
29959         }
29960         return p;
29961     },
29962
29963     /**
29964      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
29965      */
29966     collapse : function(){
29967         
29968     },
29969
29970     // private
29971     collapseIf : function(e){
29972         
29973     },
29974
29975     /**
29976      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
29977      */
29978     expand : function(){
29979         
29980     } ,
29981
29982     // private
29983      
29984
29985     /** 
29986     * @cfg {Boolean} grow 
29987     * @hide 
29988     */
29989     /** 
29990     * @cfg {Number} growMin 
29991     * @hide 
29992     */
29993     /** 
29994     * @cfg {Number} growMax 
29995     * @hide 
29996     */
29997     /**
29998      * @hide
29999      * @method autoSize
30000      */
30001     
30002     setWidth : function()
30003     {
30004         
30005     },
30006     getResizeEl : function(){
30007         return this.el;
30008     }
30009 });//<script type="text/javasscript">
30010  
30011
30012 /**
30013  * @class Roo.DDView
30014  * A DnD enabled version of Roo.View.
30015  * @param {Element/String} container The Element in which to create the View.
30016  * @param {String} tpl The template string used to create the markup for each element of the View
30017  * @param {Object} config The configuration properties. These include all the config options of
30018  * {@link Roo.View} plus some specific to this class.<br>
30019  * <p>
30020  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30021  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30022  * <p>
30023  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30024 .x-view-drag-insert-above {
30025         border-top:1px dotted #3366cc;
30026 }
30027 .x-view-drag-insert-below {
30028         border-bottom:1px dotted #3366cc;
30029 }
30030 </code></pre>
30031  * 
30032  */
30033  
30034 Roo.DDView = function(container, tpl, config) {
30035     Roo.DDView.superclass.constructor.apply(this, arguments);
30036     this.getEl().setStyle("outline", "0px none");
30037     this.getEl().unselectable();
30038     if (this.dragGroup) {
30039         this.setDraggable(this.dragGroup.split(","));
30040     }
30041     if (this.dropGroup) {
30042         this.setDroppable(this.dropGroup.split(","));
30043     }
30044     if (this.deletable) {
30045         this.setDeletable();
30046     }
30047     this.isDirtyFlag = false;
30048         this.addEvents({
30049                 "drop" : true
30050         });
30051 };
30052
30053 Roo.extend(Roo.DDView, Roo.View, {
30054 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30055 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30056 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30057 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30058
30059         isFormField: true,
30060
30061         reset: Roo.emptyFn,
30062         
30063         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30064
30065         validate: function() {
30066                 return true;
30067         },
30068         
30069         destroy: function() {
30070                 this.purgeListeners();
30071                 this.getEl.removeAllListeners();
30072                 this.getEl().remove();
30073                 if (this.dragZone) {
30074                         if (this.dragZone.destroy) {
30075                                 this.dragZone.destroy();
30076                         }
30077                 }
30078                 if (this.dropZone) {
30079                         if (this.dropZone.destroy) {
30080                                 this.dropZone.destroy();
30081                         }
30082                 }
30083         },
30084
30085 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30086         getName: function() {
30087                 return this.name;
30088         },
30089
30090 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30091         setValue: function(v) {
30092                 if (!this.store) {
30093                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30094                 }
30095                 var data = {};
30096                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30097                 this.store.proxy = new Roo.data.MemoryProxy(data);
30098                 this.store.load();
30099         },
30100
30101 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30102         getValue: function() {
30103                 var result = '(';
30104                 this.store.each(function(rec) {
30105                         result += rec.id + ',';
30106                 });
30107                 return result.substr(0, result.length - 1) + ')';
30108         },
30109         
30110         getIds: function() {
30111                 var i = 0, result = new Array(this.store.getCount());
30112                 this.store.each(function(rec) {
30113                         result[i++] = rec.id;
30114                 });
30115                 return result;
30116         },
30117         
30118         isDirty: function() {
30119                 return this.isDirtyFlag;
30120         },
30121
30122 /**
30123  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30124  *      whole Element becomes the target, and this causes the drop gesture to append.
30125  */
30126     getTargetFromEvent : function(e) {
30127                 var target = e.getTarget();
30128                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30129                 target = target.parentNode;
30130                 }
30131                 if (!target) {
30132                         target = this.el.dom.lastChild || this.el.dom;
30133                 }
30134                 return target;
30135     },
30136
30137 /**
30138  *      Create the drag data which consists of an object which has the property "ddel" as
30139  *      the drag proxy element. 
30140  */
30141     getDragData : function(e) {
30142         var target = this.findItemFromChild(e.getTarget());
30143                 if(target) {
30144                         this.handleSelection(e);
30145                         var selNodes = this.getSelectedNodes();
30146             var dragData = {
30147                 source: this,
30148                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30149                 nodes: selNodes,
30150                 records: []
30151                         };
30152                         var selectedIndices = this.getSelectedIndexes();
30153                         for (var i = 0; i < selectedIndices.length; i++) {
30154                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30155                         }
30156                         if (selNodes.length == 1) {
30157                                 dragData.ddel = target.cloneNode(true); // the div element
30158                         } else {
30159                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30160                                 div.className = 'multi-proxy';
30161                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30162                                         div.appendChild(selNodes[i].cloneNode(true));
30163                                 }
30164                                 dragData.ddel = div;
30165                         }
30166             //console.log(dragData)
30167             //console.log(dragData.ddel.innerHTML)
30168                         return dragData;
30169                 }
30170         //console.log('nodragData')
30171                 return false;
30172     },
30173     
30174 /**     Specify to which ddGroup items in this DDView may be dragged. */
30175     setDraggable: function(ddGroup) {
30176         if (ddGroup instanceof Array) {
30177                 Roo.each(ddGroup, this.setDraggable, this);
30178                 return;
30179         }
30180         if (this.dragZone) {
30181                 this.dragZone.addToGroup(ddGroup);
30182         } else {
30183                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30184                                 containerScroll: true,
30185                                 ddGroup: ddGroup 
30186
30187                         });
30188 //                      Draggability implies selection. DragZone's mousedown selects the element.
30189                         if (!this.multiSelect) { this.singleSelect = true; }
30190
30191 //                      Wire the DragZone's handlers up to methods in *this*
30192                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30193                 }
30194     },
30195
30196 /**     Specify from which ddGroup this DDView accepts drops. */
30197     setDroppable: function(ddGroup) {
30198         if (ddGroup instanceof Array) {
30199                 Roo.each(ddGroup, this.setDroppable, this);
30200                 return;
30201         }
30202         if (this.dropZone) {
30203                 this.dropZone.addToGroup(ddGroup);
30204         } else {
30205                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30206                                 containerScroll: true,
30207                                 ddGroup: ddGroup
30208                         });
30209
30210 //                      Wire the DropZone's handlers up to methods in *this*
30211                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30212                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30213                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30214                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30215                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30216                 }
30217     },
30218
30219 /**     Decide whether to drop above or below a View node. */
30220     getDropPoint : function(e, n, dd){
30221         if (n == this.el.dom) { return "above"; }
30222                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30223                 var c = t + (b - t) / 2;
30224                 var y = Roo.lib.Event.getPageY(e);
30225                 if(y <= c) {
30226                         return "above";
30227                 }else{
30228                         return "below";
30229                 }
30230     },
30231
30232     onNodeEnter : function(n, dd, e, data){
30233                 return false;
30234     },
30235     
30236     onNodeOver : function(n, dd, e, data){
30237                 var pt = this.getDropPoint(e, n, dd);
30238                 // set the insert point style on the target node
30239                 var dragElClass = this.dropNotAllowed;
30240                 if (pt) {
30241                         var targetElClass;
30242                         if (pt == "above"){
30243                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30244                                 targetElClass = "x-view-drag-insert-above";
30245                         } else {
30246                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30247                                 targetElClass = "x-view-drag-insert-below";
30248                         }
30249                         if (this.lastInsertClass != targetElClass){
30250                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30251                                 this.lastInsertClass = targetElClass;
30252                         }
30253                 }
30254                 return dragElClass;
30255         },
30256
30257     onNodeOut : function(n, dd, e, data){
30258                 this.removeDropIndicators(n);
30259     },
30260
30261     onNodeDrop : function(n, dd, e, data){
30262         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30263                 return false;
30264         }
30265         var pt = this.getDropPoint(e, n, dd);
30266                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30267                 if (pt == "below") { insertAt++; }
30268                 for (var i = 0; i < data.records.length; i++) {
30269                         var r = data.records[i];
30270                         var dup = this.store.getById(r.id);
30271                         if (dup && (dd != this.dragZone)) {
30272                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30273                         } else {
30274                                 if (data.copy) {
30275                                         this.store.insert(insertAt++, r.copy());
30276                                 } else {
30277                                         data.source.isDirtyFlag = true;
30278                                         r.store.remove(r);
30279                                         this.store.insert(insertAt++, r);
30280                                 }
30281                                 this.isDirtyFlag = true;
30282                         }
30283                 }
30284                 this.dragZone.cachedTarget = null;
30285                 return true;
30286     },
30287
30288     removeDropIndicators : function(n){
30289                 if(n){
30290                         Roo.fly(n).removeClass([
30291                                 "x-view-drag-insert-above",
30292                                 "x-view-drag-insert-below"]);
30293                         this.lastInsertClass = "_noclass";
30294                 }
30295     },
30296
30297 /**
30298  *      Utility method. Add a delete option to the DDView's context menu.
30299  *      @param {String} imageUrl The URL of the "delete" icon image.
30300  */
30301         setDeletable: function(imageUrl) {
30302                 if (!this.singleSelect && !this.multiSelect) {
30303                         this.singleSelect = true;
30304                 }
30305                 var c = this.getContextMenu();
30306                 this.contextMenu.on("itemclick", function(item) {
30307                         switch (item.id) {
30308                                 case "delete":
30309                                         this.remove(this.getSelectedIndexes());
30310                                         break;
30311                         }
30312                 }, this);
30313                 this.contextMenu.add({
30314                         icon: imageUrl,
30315                         id: "delete",
30316                         text: 'Delete'
30317                 });
30318         },
30319         
30320 /**     Return the context menu for this DDView. */
30321         getContextMenu: function() {
30322                 if (!this.contextMenu) {
30323 //                      Create the View's context menu
30324                         this.contextMenu = new Roo.menu.Menu({
30325                                 id: this.id + "-contextmenu"
30326                         });
30327                         this.el.on("contextmenu", this.showContextMenu, this);
30328                 }
30329                 return this.contextMenu;
30330         },
30331         
30332         disableContextMenu: function() {
30333                 if (this.contextMenu) {
30334                         this.el.un("contextmenu", this.showContextMenu, this);
30335                 }
30336         },
30337
30338         showContextMenu: function(e, item) {
30339         item = this.findItemFromChild(e.getTarget());
30340                 if (item) {
30341                         e.stopEvent();
30342                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30343                         this.contextMenu.showAt(e.getXY());
30344             }
30345     },
30346
30347 /**
30348  *      Remove {@link Roo.data.Record}s at the specified indices.
30349  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30350  */
30351     remove: function(selectedIndices) {
30352                 selectedIndices = [].concat(selectedIndices);
30353                 for (var i = 0; i < selectedIndices.length; i++) {
30354                         var rec = this.store.getAt(selectedIndices[i]);
30355                         this.store.remove(rec);
30356                 }
30357     },
30358
30359 /**
30360  *      Double click fires the event, but also, if this is draggable, and there is only one other
30361  *      related DropZone, it transfers the selected node.
30362  */
30363     onDblClick : function(e){
30364         var item = this.findItemFromChild(e.getTarget());
30365         if(item){
30366             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30367                 return false;
30368             }
30369             if (this.dragGroup) {
30370                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30371                     while (targets.indexOf(this.dropZone) > -1) {
30372                             targets.remove(this.dropZone);
30373                                 }
30374                     if (targets.length == 1) {
30375                                         this.dragZone.cachedTarget = null;
30376                         var el = Roo.get(targets[0].getEl());
30377                         var box = el.getBox(true);
30378                         targets[0].onNodeDrop(el.dom, {
30379                                 target: el.dom,
30380                                 xy: [box.x, box.y + box.height - 1]
30381                         }, null, this.getDragData(e));
30382                     }
30383                 }
30384         }
30385     },
30386     
30387     handleSelection: function(e) {
30388                 this.dragZone.cachedTarget = null;
30389         var item = this.findItemFromChild(e.getTarget());
30390         if (!item) {
30391                 this.clearSelections(true);
30392                 return;
30393         }
30394                 if (item && (this.multiSelect || this.singleSelect)){
30395                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30396                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30397                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30398                                 this.unselect(item);
30399                         } else {
30400                                 this.select(item, this.multiSelect && e.ctrlKey);
30401                                 this.lastSelection = item;
30402                         }
30403                 }
30404     },
30405
30406     onItemClick : function(item, index, e){
30407                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30408                         return false;
30409                 }
30410                 return true;
30411     },
30412
30413     unselect : function(nodeInfo, suppressEvent){
30414                 var node = this.getNode(nodeInfo);
30415                 if(node && this.isSelected(node)){
30416                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30417                                 Roo.fly(node).removeClass(this.selectedClass);
30418                                 this.selections.remove(node);
30419                                 if(!suppressEvent){
30420                                         this.fireEvent("selectionchange", this, this.selections);
30421                                 }
30422                         }
30423                 }
30424     }
30425 });
30426 /*
30427  * Based on:
30428  * Ext JS Library 1.1.1
30429  * Copyright(c) 2006-2007, Ext JS, LLC.
30430  *
30431  * Originally Released Under LGPL - original licence link has changed is not relivant.
30432  *
30433  * Fork - LGPL
30434  * <script type="text/javascript">
30435  */
30436  
30437 /**
30438  * @class Roo.LayoutManager
30439  * @extends Roo.util.Observable
30440  * Base class for layout managers.
30441  */
30442 Roo.LayoutManager = function(container, config){
30443     Roo.LayoutManager.superclass.constructor.call(this);
30444     this.el = Roo.get(container);
30445     // ie scrollbar fix
30446     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30447         document.body.scroll = "no";
30448     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30449         this.el.position('relative');
30450     }
30451     this.id = this.el.id;
30452     this.el.addClass("x-layout-container");
30453     /** false to disable window resize monitoring @type Boolean */
30454     this.monitorWindowResize = true;
30455     this.regions = {};
30456     this.addEvents({
30457         /**
30458          * @event layout
30459          * Fires when a layout is performed. 
30460          * @param {Roo.LayoutManager} this
30461          */
30462         "layout" : true,
30463         /**
30464          * @event regionresized
30465          * Fires when the user resizes a region. 
30466          * @param {Roo.LayoutRegion} region The resized region
30467          * @param {Number} newSize The new size (width for east/west, height for north/south)
30468          */
30469         "regionresized" : true,
30470         /**
30471          * @event regioncollapsed
30472          * Fires when a region is collapsed. 
30473          * @param {Roo.LayoutRegion} region The collapsed region
30474          */
30475         "regioncollapsed" : true,
30476         /**
30477          * @event regionexpanded
30478          * Fires when a region is expanded.  
30479          * @param {Roo.LayoutRegion} region The expanded region
30480          */
30481         "regionexpanded" : true
30482     });
30483     this.updating = false;
30484     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30485 };
30486
30487 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30488     /**
30489      * Returns true if this layout is currently being updated
30490      * @return {Boolean}
30491      */
30492     isUpdating : function(){
30493         return this.updating; 
30494     },
30495     
30496     /**
30497      * Suspend the LayoutManager from doing auto-layouts while
30498      * making multiple add or remove calls
30499      */
30500     beginUpdate : function(){
30501         this.updating = true;    
30502     },
30503     
30504     /**
30505      * Restore auto-layouts and optionally disable the manager from performing a layout
30506      * @param {Boolean} noLayout true to disable a layout update 
30507      */
30508     endUpdate : function(noLayout){
30509         this.updating = false;
30510         if(!noLayout){
30511             this.layout();
30512         }    
30513     },
30514     
30515     layout: function(){
30516         
30517     },
30518     
30519     onRegionResized : function(region, newSize){
30520         this.fireEvent("regionresized", region, newSize);
30521         this.layout();
30522     },
30523     
30524     onRegionCollapsed : function(region){
30525         this.fireEvent("regioncollapsed", region);
30526     },
30527     
30528     onRegionExpanded : function(region){
30529         this.fireEvent("regionexpanded", region);
30530     },
30531         
30532     /**
30533      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30534      * performs box-model adjustments.
30535      * @return {Object} The size as an object {width: (the width), height: (the height)}
30536      */
30537     getViewSize : function(){
30538         var size;
30539         if(this.el.dom != document.body){
30540             size = this.el.getSize();
30541         }else{
30542             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30543         }
30544         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30545         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30546         return size;
30547     },
30548     
30549     /**
30550      * Returns the Element this layout is bound to.
30551      * @return {Roo.Element}
30552      */
30553     getEl : function(){
30554         return this.el;
30555     },
30556     
30557     /**
30558      * Returns the specified region.
30559      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30560      * @return {Roo.LayoutRegion}
30561      */
30562     getRegion : function(target){
30563         return this.regions[target.toLowerCase()];
30564     },
30565     
30566     onWindowResize : function(){
30567         if(this.monitorWindowResize){
30568             this.layout();
30569         }
30570     }
30571 });/*
30572  * Based on:
30573  * Ext JS Library 1.1.1
30574  * Copyright(c) 2006-2007, Ext JS, LLC.
30575  *
30576  * Originally Released Under LGPL - original licence link has changed is not relivant.
30577  *
30578  * Fork - LGPL
30579  * <script type="text/javascript">
30580  */
30581 /**
30582  * @class Roo.BorderLayout
30583  * @extends Roo.LayoutManager
30584  * @children Roo.ContentPanel
30585  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30586  * please see: <br><br>
30587  * <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>
30588  * <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>
30589  * Example:
30590  <pre><code>
30591  var layout = new Roo.BorderLayout(document.body, {
30592     north: {
30593         initialSize: 25,
30594         titlebar: false
30595     },
30596     west: {
30597         split:true,
30598         initialSize: 200,
30599         minSize: 175,
30600         maxSize: 400,
30601         titlebar: true,
30602         collapsible: true
30603     },
30604     east: {
30605         split:true,
30606         initialSize: 202,
30607         minSize: 175,
30608         maxSize: 400,
30609         titlebar: true,
30610         collapsible: true
30611     },
30612     south: {
30613         split:true,
30614         initialSize: 100,
30615         minSize: 100,
30616         maxSize: 200,
30617         titlebar: true,
30618         collapsible: true
30619     },
30620     center: {
30621         titlebar: true,
30622         autoScroll:true,
30623         resizeTabs: true,
30624         minTabWidth: 50,
30625         preferredTabWidth: 150
30626     }
30627 });
30628
30629 // shorthand
30630 var CP = Roo.ContentPanel;
30631
30632 layout.beginUpdate();
30633 layout.add("north", new CP("north", "North"));
30634 layout.add("south", new CP("south", {title: "South", closable: true}));
30635 layout.add("west", new CP("west", {title: "West"}));
30636 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30637 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30638 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30639 layout.getRegion("center").showPanel("center1");
30640 layout.endUpdate();
30641 </code></pre>
30642
30643 <b>The container the layout is rendered into can be either the body element or any other element.
30644 If it is not the body element, the container needs to either be an absolute positioned element,
30645 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30646 the container size if it is not the body element.</b>
30647
30648 * @constructor
30649 * Create a new BorderLayout
30650 * @param {String/HTMLElement/Element} container The container this layout is bound to
30651 * @param {Object} config Configuration options
30652  */
30653 Roo.BorderLayout = function(container, config){
30654     config = config || {};
30655     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30656     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30657     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30658         var target = this.factory.validRegions[i];
30659         if(config[target]){
30660             this.addRegion(target, config[target]);
30661         }
30662     }
30663 };
30664
30665 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30666         
30667         /**
30668          * @cfg {Roo.LayoutRegion} east
30669          */
30670         /**
30671          * @cfg {Roo.LayoutRegion} west
30672          */
30673         /**
30674          * @cfg {Roo.LayoutRegion} north
30675          */
30676         /**
30677          * @cfg {Roo.LayoutRegion} south
30678          */
30679         /**
30680          * @cfg {Roo.LayoutRegion} center
30681          */
30682     /**
30683      * Creates and adds a new region if it doesn't already exist.
30684      * @param {String} target The target region key (north, south, east, west or center).
30685      * @param {Object} config The regions config object
30686      * @return {BorderLayoutRegion} The new region
30687      */
30688     addRegion : function(target, config){
30689         if(!this.regions[target]){
30690             var r = this.factory.create(target, this, config);
30691             this.bindRegion(target, r);
30692         }
30693         return this.regions[target];
30694     },
30695
30696     // private (kinda)
30697     bindRegion : function(name, r){
30698         this.regions[name] = r;
30699         r.on("visibilitychange", this.layout, this);
30700         r.on("paneladded", this.layout, this);
30701         r.on("panelremoved", this.layout, this);
30702         r.on("invalidated", this.layout, this);
30703         r.on("resized", this.onRegionResized, this);
30704         r.on("collapsed", this.onRegionCollapsed, this);
30705         r.on("expanded", this.onRegionExpanded, this);
30706     },
30707
30708     /**
30709      * Performs a layout update.
30710      */
30711     layout : function(){
30712         if(this.updating) {
30713             return;
30714         }
30715         var size = this.getViewSize();
30716         var w = size.width;
30717         var h = size.height;
30718         var centerW = w;
30719         var centerH = h;
30720         var centerY = 0;
30721         var centerX = 0;
30722         //var x = 0, y = 0;
30723
30724         var rs = this.regions;
30725         var north = rs["north"];
30726         var south = rs["south"]; 
30727         var west = rs["west"];
30728         var east = rs["east"];
30729         var center = rs["center"];
30730         //if(this.hideOnLayout){ // not supported anymore
30731             //c.el.setStyle("display", "none");
30732         //}
30733         if(north && north.isVisible()){
30734             var b = north.getBox();
30735             var m = north.getMargins();
30736             b.width = w - (m.left+m.right);
30737             b.x = m.left;
30738             b.y = m.top;
30739             centerY = b.height + b.y + m.bottom;
30740             centerH -= centerY;
30741             north.updateBox(this.safeBox(b));
30742         }
30743         if(south && south.isVisible()){
30744             var b = south.getBox();
30745             var m = south.getMargins();
30746             b.width = w - (m.left+m.right);
30747             b.x = m.left;
30748             var totalHeight = (b.height + m.top + m.bottom);
30749             b.y = h - totalHeight + m.top;
30750             centerH -= totalHeight;
30751             south.updateBox(this.safeBox(b));
30752         }
30753         if(west && west.isVisible()){
30754             var b = west.getBox();
30755             var m = west.getMargins();
30756             b.height = centerH - (m.top+m.bottom);
30757             b.x = m.left;
30758             b.y = centerY + m.top;
30759             var totalWidth = (b.width + m.left + m.right);
30760             centerX += totalWidth;
30761             centerW -= totalWidth;
30762             west.updateBox(this.safeBox(b));
30763         }
30764         if(east && east.isVisible()){
30765             var b = east.getBox();
30766             var m = east.getMargins();
30767             b.height = centerH - (m.top+m.bottom);
30768             var totalWidth = (b.width + m.left + m.right);
30769             b.x = w - totalWidth + m.left;
30770             b.y = centerY + m.top;
30771             centerW -= totalWidth;
30772             east.updateBox(this.safeBox(b));
30773         }
30774         if(center){
30775             var m = center.getMargins();
30776             var centerBox = {
30777                 x: centerX + m.left,
30778                 y: centerY + m.top,
30779                 width: centerW - (m.left+m.right),
30780                 height: centerH - (m.top+m.bottom)
30781             };
30782             //if(this.hideOnLayout){
30783                 //center.el.setStyle("display", "block");
30784             //}
30785             center.updateBox(this.safeBox(centerBox));
30786         }
30787         this.el.repaint();
30788         this.fireEvent("layout", this);
30789     },
30790
30791     // private
30792     safeBox : function(box){
30793         box.width = Math.max(0, box.width);
30794         box.height = Math.max(0, box.height);
30795         return box;
30796     },
30797
30798     /**
30799      * Adds a ContentPanel (or subclass) to this layout.
30800      * @param {String} target The target region key (north, south, east, west or center).
30801      * @param {Roo.ContentPanel} panel The panel to add
30802      * @return {Roo.ContentPanel} The added panel
30803      */
30804     add : function(target, panel){
30805          
30806         target = target.toLowerCase();
30807         return this.regions[target].add(panel);
30808     },
30809
30810     /**
30811      * Remove a ContentPanel (or subclass) to this layout.
30812      * @param {String} target The target region key (north, south, east, west or center).
30813      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30814      * @return {Roo.ContentPanel} The removed panel
30815      */
30816     remove : function(target, panel){
30817         target = target.toLowerCase();
30818         return this.regions[target].remove(panel);
30819     },
30820
30821     /**
30822      * Searches all regions for a panel with the specified id
30823      * @param {String} panelId
30824      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30825      */
30826     findPanel : function(panelId){
30827         var rs = this.regions;
30828         for(var target in rs){
30829             if(typeof rs[target] != "function"){
30830                 var p = rs[target].getPanel(panelId);
30831                 if(p){
30832                     return p;
30833                 }
30834             }
30835         }
30836         return null;
30837     },
30838
30839     /**
30840      * Searches all regions for a panel with the specified id and activates (shows) it.
30841      * @param {String/ContentPanel} panelId The panels id or the panel itself
30842      * @return {Roo.ContentPanel} The shown panel or null
30843      */
30844     showPanel : function(panelId) {
30845       var rs = this.regions;
30846       for(var target in rs){
30847          var r = rs[target];
30848          if(typeof r != "function"){
30849             if(r.hasPanel(panelId)){
30850                return r.showPanel(panelId);
30851             }
30852          }
30853       }
30854       return null;
30855    },
30856
30857    /**
30858      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30859      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30860      */
30861     restoreState : function(provider){
30862         if(!provider){
30863             provider = Roo.state.Manager;
30864         }
30865         var sm = new Roo.LayoutStateManager();
30866         sm.init(this, provider);
30867     },
30868
30869     /**
30870      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30871      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30872      * a valid ContentPanel config object.  Example:
30873      * <pre><code>
30874 // Create the main layout
30875 var layout = new Roo.BorderLayout('main-ct', {
30876     west: {
30877         split:true,
30878         minSize: 175,
30879         titlebar: true
30880     },
30881     center: {
30882         title:'Components'
30883     }
30884 }, 'main-ct');
30885
30886 // Create and add multiple ContentPanels at once via configs
30887 layout.batchAdd({
30888    west: {
30889        id: 'source-files',
30890        autoCreate:true,
30891        title:'Ext Source Files',
30892        autoScroll:true,
30893        fitToFrame:true
30894    },
30895    center : {
30896        el: cview,
30897        autoScroll:true,
30898        fitToFrame:true,
30899        toolbar: tb,
30900        resizeEl:'cbody'
30901    }
30902 });
30903 </code></pre>
30904      * @param {Object} regions An object containing ContentPanel configs by region name
30905      */
30906     batchAdd : function(regions){
30907         this.beginUpdate();
30908         for(var rname in regions){
30909             var lr = this.regions[rname];
30910             if(lr){
30911                 this.addTypedPanels(lr, regions[rname]);
30912             }
30913         }
30914         this.endUpdate();
30915     },
30916
30917     // private
30918     addTypedPanels : function(lr, ps){
30919         if(typeof ps == 'string'){
30920             lr.add(new Roo.ContentPanel(ps));
30921         }
30922         else if(ps instanceof Array){
30923             for(var i =0, len = ps.length; i < len; i++){
30924                 this.addTypedPanels(lr, ps[i]);
30925             }
30926         }
30927         else if(!ps.events){ // raw config?
30928             var el = ps.el;
30929             delete ps.el; // prevent conflict
30930             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30931         }
30932         else {  // panel object assumed!
30933             lr.add(ps);
30934         }
30935     },
30936     /**
30937      * Adds a xtype elements to the layout.
30938      * <pre><code>
30939
30940 layout.addxtype({
30941        xtype : 'ContentPanel',
30942        region: 'west',
30943        items: [ .... ]
30944    }
30945 );
30946
30947 layout.addxtype({
30948         xtype : 'NestedLayoutPanel',
30949         region: 'west',
30950         layout: {
30951            center: { },
30952            west: { }   
30953         },
30954         items : [ ... list of content panels or nested layout panels.. ]
30955    }
30956 );
30957 </code></pre>
30958      * @param {Object} cfg Xtype definition of item to add.
30959      */
30960     addxtype : function(cfg)
30961     {
30962         // basically accepts a pannel...
30963         // can accept a layout region..!?!?
30964         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30965         
30966         if (!cfg.xtype.match(/Panel$/)) {
30967             return false;
30968         }
30969         var ret = false;
30970         
30971         if (typeof(cfg.region) == 'undefined') {
30972             Roo.log("Failed to add Panel, region was not set");
30973             Roo.log(cfg);
30974             return false;
30975         }
30976         var region = cfg.region;
30977         delete cfg.region;
30978         
30979           
30980         var xitems = [];
30981         if (cfg.items) {
30982             xitems = cfg.items;
30983             delete cfg.items;
30984         }
30985         var nb = false;
30986         
30987         switch(cfg.xtype) 
30988         {
30989             case 'ContentPanel':  // ContentPanel (el, cfg)
30990             case 'ScrollPanel':  // ContentPanel (el, cfg)
30991             case 'ViewPanel': 
30992                 if(cfg.autoCreate) {
30993                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30994                 } else {
30995                     var el = this.el.createChild();
30996                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30997                 }
30998                 
30999                 this.add(region, ret);
31000                 break;
31001             
31002             
31003             case 'TreePanel': // our new panel!
31004                 cfg.el = this.el.createChild();
31005                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31006                 this.add(region, ret);
31007                 break;
31008             
31009             case 'NestedLayoutPanel': 
31010                 // create a new Layout (which is  a Border Layout...
31011                 var el = this.el.createChild();
31012                 var clayout = cfg.layout;
31013                 delete cfg.layout;
31014                 clayout.items   = clayout.items  || [];
31015                 // replace this exitems with the clayout ones..
31016                 xitems = clayout.items;
31017                  
31018                 
31019                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31020                     cfg.background = false;
31021                 }
31022                 var layout = new Roo.BorderLayout(el, clayout);
31023                 
31024                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31025                 //console.log('adding nested layout panel '  + cfg.toSource());
31026                 this.add(region, ret);
31027                 nb = {}; /// find first...
31028                 break;
31029                 
31030             case 'GridPanel': 
31031             
31032                 // needs grid and region
31033                 
31034                 //var el = this.getRegion(region).el.createChild();
31035                 var el = this.el.createChild();
31036                 // create the grid first...
31037                 
31038                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31039                 delete cfg.grid;
31040                 if (region == 'center' && this.active ) {
31041                     cfg.background = false;
31042                 }
31043                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31044                 
31045                 this.add(region, ret);
31046                 if (cfg.background) {
31047                     ret.on('activate', function(gp) {
31048                         if (!gp.grid.rendered) {
31049                             gp.grid.render();
31050                         }
31051                     });
31052                 } else {
31053                     grid.render();
31054                 }
31055                 break;
31056            
31057            
31058            
31059                 
31060                 
31061                 
31062             default:
31063                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31064                     
31065                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31066                     this.add(region, ret);
31067                 } else {
31068                 
31069                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31070                     return null;
31071                 }
31072                 
31073              // GridPanel (grid, cfg)
31074             
31075         }
31076         this.beginUpdate();
31077         // add children..
31078         var region = '';
31079         var abn = {};
31080         Roo.each(xitems, function(i)  {
31081             region = nb && i.region ? i.region : false;
31082             
31083             var add = ret.addxtype(i);
31084            
31085             if (region) {
31086                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31087                 if (!i.background) {
31088                     abn[region] = nb[region] ;
31089                 }
31090             }
31091             
31092         });
31093         this.endUpdate();
31094
31095         // make the last non-background panel active..
31096         //if (nb) { Roo.log(abn); }
31097         if (nb) {
31098             
31099             for(var r in abn) {
31100                 region = this.getRegion(r);
31101                 if (region) {
31102                     // tried using nb[r], but it does not work..
31103                      
31104                     region.showPanel(abn[r]);
31105                    
31106                 }
31107             }
31108         }
31109         return ret;
31110         
31111     }
31112 });
31113
31114 /**
31115  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31116  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31117  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31118  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31119  * <pre><code>
31120 // shorthand
31121 var CP = Roo.ContentPanel;
31122
31123 var layout = Roo.BorderLayout.create({
31124     north: {
31125         initialSize: 25,
31126         titlebar: false,
31127         panels: [new CP("north", "North")]
31128     },
31129     west: {
31130         split:true,
31131         initialSize: 200,
31132         minSize: 175,
31133         maxSize: 400,
31134         titlebar: true,
31135         collapsible: true,
31136         panels: [new CP("west", {title: "West"})]
31137     },
31138     east: {
31139         split:true,
31140         initialSize: 202,
31141         minSize: 175,
31142         maxSize: 400,
31143         titlebar: true,
31144         collapsible: true,
31145         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31146     },
31147     south: {
31148         split:true,
31149         initialSize: 100,
31150         minSize: 100,
31151         maxSize: 200,
31152         titlebar: true,
31153         collapsible: true,
31154         panels: [new CP("south", {title: "South", closable: true})]
31155     },
31156     center: {
31157         titlebar: true,
31158         autoScroll:true,
31159         resizeTabs: true,
31160         minTabWidth: 50,
31161         preferredTabWidth: 150,
31162         panels: [
31163             new CP("center1", {title: "Close Me", closable: true}),
31164             new CP("center2", {title: "Center Panel", closable: false})
31165         ]
31166     }
31167 }, document.body);
31168
31169 layout.getRegion("center").showPanel("center1");
31170 </code></pre>
31171  * @param config
31172  * @param targetEl
31173  */
31174 Roo.BorderLayout.create = function(config, targetEl){
31175     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31176     layout.beginUpdate();
31177     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31178     for(var j = 0, jlen = regions.length; j < jlen; j++){
31179         var lr = regions[j];
31180         if(layout.regions[lr] && config[lr].panels){
31181             var r = layout.regions[lr];
31182             var ps = config[lr].panels;
31183             layout.addTypedPanels(r, ps);
31184         }
31185     }
31186     layout.endUpdate();
31187     return layout;
31188 };
31189
31190 // private
31191 Roo.BorderLayout.RegionFactory = {
31192     // private
31193     validRegions : ["north","south","east","west","center"],
31194
31195     // private
31196     create : function(target, mgr, config){
31197         target = target.toLowerCase();
31198         if(config.lightweight || config.basic){
31199             return new Roo.BasicLayoutRegion(mgr, config, target);
31200         }
31201         switch(target){
31202             case "north":
31203                 return new Roo.NorthLayoutRegion(mgr, config);
31204             case "south":
31205                 return new Roo.SouthLayoutRegion(mgr, config);
31206             case "east":
31207                 return new Roo.EastLayoutRegion(mgr, config);
31208             case "west":
31209                 return new Roo.WestLayoutRegion(mgr, config);
31210             case "center":
31211                 return new Roo.CenterLayoutRegion(mgr, config);
31212         }
31213         throw 'Layout region "'+target+'" not supported.';
31214     }
31215 };/*
31216  * Based on:
31217  * Ext JS Library 1.1.1
31218  * Copyright(c) 2006-2007, Ext JS, LLC.
31219  *
31220  * Originally Released Under LGPL - original licence link has changed is not relivant.
31221  *
31222  * Fork - LGPL
31223  * <script type="text/javascript">
31224  */
31225  
31226 /**
31227  * @class Roo.BasicLayoutRegion
31228  * @extends Roo.util.Observable
31229  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31230  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31231  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31232  */
31233 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31234     this.mgr = mgr;
31235     this.position  = pos;
31236     this.events = {
31237         /**
31238          * @scope Roo.BasicLayoutRegion
31239          */
31240         
31241         /**
31242          * @event beforeremove
31243          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31244          * @param {Roo.LayoutRegion} this
31245          * @param {Roo.ContentPanel} panel The panel
31246          * @param {Object} e The cancel event object
31247          */
31248         "beforeremove" : true,
31249         /**
31250          * @event invalidated
31251          * Fires when the layout for this region is changed.
31252          * @param {Roo.LayoutRegion} this
31253          */
31254         "invalidated" : true,
31255         /**
31256          * @event visibilitychange
31257          * Fires when this region is shown or hidden 
31258          * @param {Roo.LayoutRegion} this
31259          * @param {Boolean} visibility true or false
31260          */
31261         "visibilitychange" : true,
31262         /**
31263          * @event paneladded
31264          * Fires when a panel is added. 
31265          * @param {Roo.LayoutRegion} this
31266          * @param {Roo.ContentPanel} panel The panel
31267          */
31268         "paneladded" : true,
31269         /**
31270          * @event panelremoved
31271          * Fires when a panel is removed. 
31272          * @param {Roo.LayoutRegion} this
31273          * @param {Roo.ContentPanel} panel The panel
31274          */
31275         "panelremoved" : true,
31276         /**
31277          * @event beforecollapse
31278          * Fires when this region before collapse.
31279          * @param {Roo.LayoutRegion} this
31280          */
31281         "beforecollapse" : true,
31282         /**
31283          * @event collapsed
31284          * Fires when this region is collapsed.
31285          * @param {Roo.LayoutRegion} this
31286          */
31287         "collapsed" : true,
31288         /**
31289          * @event expanded
31290          * Fires when this region is expanded.
31291          * @param {Roo.LayoutRegion} this
31292          */
31293         "expanded" : true,
31294         /**
31295          * @event slideshow
31296          * Fires when this region is slid into view.
31297          * @param {Roo.LayoutRegion} this
31298          */
31299         "slideshow" : true,
31300         /**
31301          * @event slidehide
31302          * Fires when this region slides out of view. 
31303          * @param {Roo.LayoutRegion} this
31304          */
31305         "slidehide" : true,
31306         /**
31307          * @event panelactivated
31308          * Fires when a panel is activated. 
31309          * @param {Roo.LayoutRegion} this
31310          * @param {Roo.ContentPanel} panel The activated panel
31311          */
31312         "panelactivated" : true,
31313         /**
31314          * @event resized
31315          * Fires when the user resizes this region. 
31316          * @param {Roo.LayoutRegion} this
31317          * @param {Number} newSize The new size (width for east/west, height for north/south)
31318          */
31319         "resized" : true
31320     };
31321     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31322     this.panels = new Roo.util.MixedCollection();
31323     this.panels.getKey = this.getPanelId.createDelegate(this);
31324     this.box = null;
31325     this.activePanel = null;
31326     // ensure listeners are added...
31327     
31328     if (config.listeners || config.events) {
31329         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31330             listeners : config.listeners || {},
31331             events : config.events || {}
31332         });
31333     }
31334     
31335     if(skipConfig !== true){
31336         this.applyConfig(config);
31337     }
31338 };
31339
31340 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31341     getPanelId : function(p){
31342         return p.getId();
31343     },
31344     
31345     applyConfig : function(config){
31346         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31347         this.config = config;
31348         
31349     },
31350     
31351     /**
31352      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31353      * the width, for horizontal (north, south) the height.
31354      * @param {Number} newSize The new width or height
31355      */
31356     resizeTo : function(newSize){
31357         var el = this.el ? this.el :
31358                  (this.activePanel ? this.activePanel.getEl() : null);
31359         if(el){
31360             switch(this.position){
31361                 case "east":
31362                 case "west":
31363                     el.setWidth(newSize);
31364                     this.fireEvent("resized", this, newSize);
31365                 break;
31366                 case "north":
31367                 case "south":
31368                     el.setHeight(newSize);
31369                     this.fireEvent("resized", this, newSize);
31370                 break;                
31371             }
31372         }
31373     },
31374     
31375     getBox : function(){
31376         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31377     },
31378     
31379     getMargins : function(){
31380         return this.margins;
31381     },
31382     
31383     updateBox : function(box){
31384         this.box = box;
31385         var el = this.activePanel.getEl();
31386         el.dom.style.left = box.x + "px";
31387         el.dom.style.top = box.y + "px";
31388         this.activePanel.setSize(box.width, box.height);
31389     },
31390     
31391     /**
31392      * Returns the container element for this region.
31393      * @return {Roo.Element}
31394      */
31395     getEl : function(){
31396         return this.activePanel;
31397     },
31398     
31399     /**
31400      * Returns true if this region is currently visible.
31401      * @return {Boolean}
31402      */
31403     isVisible : function(){
31404         return this.activePanel ? true : false;
31405     },
31406     
31407     setActivePanel : function(panel){
31408         panel = this.getPanel(panel);
31409         if(this.activePanel && this.activePanel != panel){
31410             this.activePanel.setActiveState(false);
31411             this.activePanel.getEl().setLeftTop(-10000,-10000);
31412         }
31413         this.activePanel = panel;
31414         panel.setActiveState(true);
31415         if(this.box){
31416             panel.setSize(this.box.width, this.box.height);
31417         }
31418         this.fireEvent("panelactivated", this, panel);
31419         this.fireEvent("invalidated");
31420     },
31421     
31422     /**
31423      * Show the specified panel.
31424      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31425      * @return {Roo.ContentPanel} The shown panel or null
31426      */
31427     showPanel : function(panel){
31428         if(panel = this.getPanel(panel)){
31429             this.setActivePanel(panel);
31430         }
31431         return panel;
31432     },
31433     
31434     /**
31435      * Get the active panel for this region.
31436      * @return {Roo.ContentPanel} The active panel or null
31437      */
31438     getActivePanel : function(){
31439         return this.activePanel;
31440     },
31441     
31442     /**
31443      * Add the passed ContentPanel(s)
31444      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31445      * @return {Roo.ContentPanel} The panel added (if only one was added)
31446      */
31447     add : function(panel){
31448         if(arguments.length > 1){
31449             for(var i = 0, len = arguments.length; i < len; i++) {
31450                 this.add(arguments[i]);
31451             }
31452             return null;
31453         }
31454         if(this.hasPanel(panel)){
31455             this.showPanel(panel);
31456             return panel;
31457         }
31458         var el = panel.getEl();
31459         if(el.dom.parentNode != this.mgr.el.dom){
31460             this.mgr.el.dom.appendChild(el.dom);
31461         }
31462         if(panel.setRegion){
31463             panel.setRegion(this);
31464         }
31465         this.panels.add(panel);
31466         el.setStyle("position", "absolute");
31467         if(!panel.background){
31468             this.setActivePanel(panel);
31469             if(this.config.initialSize && this.panels.getCount()==1){
31470                 this.resizeTo(this.config.initialSize);
31471             }
31472         }
31473         this.fireEvent("paneladded", this, panel);
31474         return panel;
31475     },
31476     
31477     /**
31478      * Returns true if the panel is in this region.
31479      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31480      * @return {Boolean}
31481      */
31482     hasPanel : function(panel){
31483         if(typeof panel == "object"){ // must be panel obj
31484             panel = panel.getId();
31485         }
31486         return this.getPanel(panel) ? true : false;
31487     },
31488     
31489     /**
31490      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31491      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31492      * @param {Boolean} preservePanel Overrides the config preservePanel option
31493      * @return {Roo.ContentPanel} The panel that was removed
31494      */
31495     remove : function(panel, preservePanel){
31496         panel = this.getPanel(panel);
31497         if(!panel){
31498             return null;
31499         }
31500         var e = {};
31501         this.fireEvent("beforeremove", this, panel, e);
31502         if(e.cancel === true){
31503             return null;
31504         }
31505         var panelId = panel.getId();
31506         this.panels.removeKey(panelId);
31507         return panel;
31508     },
31509     
31510     /**
31511      * Returns the panel specified or null if it's not in this region.
31512      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31513      * @return {Roo.ContentPanel}
31514      */
31515     getPanel : function(id){
31516         if(typeof id == "object"){ // must be panel obj
31517             return id;
31518         }
31519         return this.panels.get(id);
31520     },
31521     
31522     /**
31523      * Returns this regions position (north/south/east/west/center).
31524      * @return {String} 
31525      */
31526     getPosition: function(){
31527         return this.position;    
31528     }
31529 });/*
31530  * Based on:
31531  * Ext JS Library 1.1.1
31532  * Copyright(c) 2006-2007, Ext JS, LLC.
31533  *
31534  * Originally Released Under LGPL - original licence link has changed is not relivant.
31535  *
31536  * Fork - LGPL
31537  * <script type="text/javascript">
31538  */
31539  
31540 /**
31541  * @class Roo.LayoutRegion
31542  * @extends Roo.BasicLayoutRegion
31543  * This class represents a region in a layout manager.
31544  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31545  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31546  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31547  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31548  * @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})
31549  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31550  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31551  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31552  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31553  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31554  * @cfg {String}    title           The title for the region (overrides panel titles)
31555  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31556  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31557  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31558  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31559  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31560  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31561  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31562  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31563  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31564  * @cfg {Boolean}   showPin         True to show a pin button
31565  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31566  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31567  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31568  * @cfg {Number}    width           For East/West panels
31569  * @cfg {Number}    height          For North/South panels
31570  * @cfg {Boolean}   split           To show the splitter
31571  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31572  */
31573 Roo.LayoutRegion = function(mgr, config, pos){
31574     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31575     var dh = Roo.DomHelper;
31576     /** This region's container element 
31577     * @type Roo.Element */
31578     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31579     /** This region's title element 
31580     * @type Roo.Element */
31581
31582     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31583         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31584         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31585     ]}, true);
31586     this.titleEl.enableDisplayMode();
31587     /** This region's title text element 
31588     * @type HTMLElement */
31589     this.titleTextEl = this.titleEl.dom.firstChild;
31590     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31591     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31592     this.closeBtn.enableDisplayMode();
31593     this.closeBtn.on("click", this.closeClicked, this);
31594     this.closeBtn.hide();
31595
31596     this.createBody(config);
31597     this.visible = true;
31598     this.collapsed = false;
31599
31600     if(config.hideWhenEmpty){
31601         this.hide();
31602         this.on("paneladded", this.validateVisibility, this);
31603         this.on("panelremoved", this.validateVisibility, this);
31604     }
31605     this.applyConfig(config);
31606 };
31607
31608 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31609
31610     createBody : function(){
31611         /** This region's body element 
31612         * @type Roo.Element */
31613         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31614     },
31615
31616     applyConfig : function(c){
31617         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31618             var dh = Roo.DomHelper;
31619             if(c.titlebar !== false){
31620                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31621                 this.collapseBtn.on("click", this.collapse, this);
31622                 this.collapseBtn.enableDisplayMode();
31623
31624                 if(c.showPin === true || this.showPin){
31625                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31626                     this.stickBtn.enableDisplayMode();
31627                     this.stickBtn.on("click", this.expand, this);
31628                     this.stickBtn.hide();
31629                 }
31630             }
31631             /** This region's collapsed element
31632             * @type Roo.Element */
31633             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31634                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31635             ]}, true);
31636             if(c.floatable !== false){
31637                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31638                this.collapsedEl.on("click", this.collapseClick, this);
31639             }
31640
31641             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31642                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31643                    id: "message", unselectable: "on", style:{"float":"left"}});
31644                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31645              }
31646             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31647             this.expandBtn.on("click", this.expand, this);
31648         }
31649         if(this.collapseBtn){
31650             this.collapseBtn.setVisible(c.collapsible == true);
31651         }
31652         this.cmargins = c.cmargins || this.cmargins ||
31653                          (this.position == "west" || this.position == "east" ?
31654                              {top: 0, left: 2, right:2, bottom: 0} :
31655                              {top: 2, left: 0, right:0, bottom: 2});
31656         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31657         this.bottomTabs = c.tabPosition != "top";
31658         this.autoScroll = c.autoScroll || false;
31659         if(this.autoScroll){
31660             this.bodyEl.setStyle("overflow", "auto");
31661         }else{
31662             this.bodyEl.setStyle("overflow", "hidden");
31663         }
31664         //if(c.titlebar !== false){
31665             if((!c.titlebar && !c.title) || c.titlebar === false){
31666                 this.titleEl.hide();
31667             }else{
31668                 this.titleEl.show();
31669                 if(c.title){
31670                     this.titleTextEl.innerHTML = c.title;
31671                 }
31672             }
31673         //}
31674         this.duration = c.duration || .30;
31675         this.slideDuration = c.slideDuration || .45;
31676         this.config = c;
31677         if(c.collapsed){
31678             this.collapse(true);
31679         }
31680         if(c.hidden){
31681             this.hide();
31682         }
31683     },
31684     /**
31685      * Returns true if this region is currently visible.
31686      * @return {Boolean}
31687      */
31688     isVisible : function(){
31689         return this.visible;
31690     },
31691
31692     /**
31693      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31694      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31695      */
31696     setCollapsedTitle : function(title){
31697         title = title || "&#160;";
31698         if(this.collapsedTitleTextEl){
31699             this.collapsedTitleTextEl.innerHTML = title;
31700         }
31701     },
31702
31703     getBox : function(){
31704         var b;
31705         if(!this.collapsed){
31706             b = this.el.getBox(false, true);
31707         }else{
31708             b = this.collapsedEl.getBox(false, true);
31709         }
31710         return b;
31711     },
31712
31713     getMargins : function(){
31714         return this.collapsed ? this.cmargins : this.margins;
31715     },
31716
31717     highlight : function(){
31718         this.el.addClass("x-layout-panel-dragover");
31719     },
31720
31721     unhighlight : function(){
31722         this.el.removeClass("x-layout-panel-dragover");
31723     },
31724
31725     updateBox : function(box){
31726         this.box = box;
31727         if(!this.collapsed){
31728             this.el.dom.style.left = box.x + "px";
31729             this.el.dom.style.top = box.y + "px";
31730             this.updateBody(box.width, box.height);
31731         }else{
31732             this.collapsedEl.dom.style.left = box.x + "px";
31733             this.collapsedEl.dom.style.top = box.y + "px";
31734             this.collapsedEl.setSize(box.width, box.height);
31735         }
31736         if(this.tabs){
31737             this.tabs.autoSizeTabs();
31738         }
31739     },
31740
31741     updateBody : function(w, h){
31742         if(w !== null){
31743             this.el.setWidth(w);
31744             w -= this.el.getBorderWidth("rl");
31745             if(this.config.adjustments){
31746                 w += this.config.adjustments[0];
31747             }
31748         }
31749         if(h !== null){
31750             this.el.setHeight(h);
31751             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31752             h -= this.el.getBorderWidth("tb");
31753             if(this.config.adjustments){
31754                 h += this.config.adjustments[1];
31755             }
31756             this.bodyEl.setHeight(h);
31757             if(this.tabs){
31758                 h = this.tabs.syncHeight(h);
31759             }
31760         }
31761         if(this.panelSize){
31762             w = w !== null ? w : this.panelSize.width;
31763             h = h !== null ? h : this.panelSize.height;
31764         }
31765         if(this.activePanel){
31766             var el = this.activePanel.getEl();
31767             w = w !== null ? w : el.getWidth();
31768             h = h !== null ? h : el.getHeight();
31769             this.panelSize = {width: w, height: h};
31770             this.activePanel.setSize(w, h);
31771         }
31772         if(Roo.isIE && this.tabs){
31773             this.tabs.el.repaint();
31774         }
31775     },
31776
31777     /**
31778      * Returns the container element for this region.
31779      * @return {Roo.Element}
31780      */
31781     getEl : function(){
31782         return this.el;
31783     },
31784
31785     /**
31786      * Hides this region.
31787      */
31788     hide : function(){
31789         if(!this.collapsed){
31790             this.el.dom.style.left = "-2000px";
31791             this.el.hide();
31792         }else{
31793             this.collapsedEl.dom.style.left = "-2000px";
31794             this.collapsedEl.hide();
31795         }
31796         this.visible = false;
31797         this.fireEvent("visibilitychange", this, false);
31798     },
31799
31800     /**
31801      * Shows this region if it was previously hidden.
31802      */
31803     show : function(){
31804         if(!this.collapsed){
31805             this.el.show();
31806         }else{
31807             this.collapsedEl.show();
31808         }
31809         this.visible = true;
31810         this.fireEvent("visibilitychange", this, true);
31811     },
31812
31813     closeClicked : function(){
31814         if(this.activePanel){
31815             this.remove(this.activePanel);
31816         }
31817     },
31818
31819     collapseClick : function(e){
31820         if(this.isSlid){
31821            e.stopPropagation();
31822            this.slideIn();
31823         }else{
31824            e.stopPropagation();
31825            this.slideOut();
31826         }
31827     },
31828
31829     /**
31830      * Collapses this region.
31831      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31832      */
31833     collapse : function(skipAnim, skipCheck){
31834         if(this.collapsed) {
31835             return;
31836         }
31837         
31838         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
31839             
31840             this.collapsed = true;
31841             if(this.split){
31842                 this.split.el.hide();
31843             }
31844             if(this.config.animate && skipAnim !== true){
31845                 this.fireEvent("invalidated", this);
31846                 this.animateCollapse();
31847             }else{
31848                 this.el.setLocation(-20000,-20000);
31849                 this.el.hide();
31850                 this.collapsedEl.show();
31851                 this.fireEvent("collapsed", this);
31852                 this.fireEvent("invalidated", this);
31853             }
31854         }
31855         
31856     },
31857
31858     animateCollapse : function(){
31859         // overridden
31860     },
31861
31862     /**
31863      * Expands this region if it was previously collapsed.
31864      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31865      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31866      */
31867     expand : function(e, skipAnim){
31868         if(e) {
31869             e.stopPropagation();
31870         }
31871         if(!this.collapsed || this.el.hasActiveFx()) {
31872             return;
31873         }
31874         if(this.isSlid){
31875             this.afterSlideIn();
31876             skipAnim = true;
31877         }
31878         this.collapsed = false;
31879         if(this.config.animate && skipAnim !== true){
31880             this.animateExpand();
31881         }else{
31882             this.el.show();
31883             if(this.split){
31884                 this.split.el.show();
31885             }
31886             this.collapsedEl.setLocation(-2000,-2000);
31887             this.collapsedEl.hide();
31888             this.fireEvent("invalidated", this);
31889             this.fireEvent("expanded", this);
31890         }
31891     },
31892
31893     animateExpand : function(){
31894         // overridden
31895     },
31896
31897     initTabs : function()
31898     {
31899         this.bodyEl.setStyle("overflow", "hidden");
31900         var ts = new Roo.TabPanel(
31901                 this.bodyEl.dom,
31902                 {
31903                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31904                     disableTooltips: this.config.disableTabTips,
31905                     toolbar : this.config.toolbar
31906                 }
31907         );
31908         if(this.config.hideTabs){
31909             ts.stripWrap.setDisplayed(false);
31910         }
31911         this.tabs = ts;
31912         ts.resizeTabs = this.config.resizeTabs === true;
31913         ts.minTabWidth = this.config.minTabWidth || 40;
31914         ts.maxTabWidth = this.config.maxTabWidth || 250;
31915         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31916         ts.monitorResize = false;
31917         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31918         ts.bodyEl.addClass('x-layout-tabs-body');
31919         this.panels.each(this.initPanelAsTab, this);
31920     },
31921
31922     initPanelAsTab : function(panel){
31923         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31924                     this.config.closeOnTab && panel.isClosable());
31925         if(panel.tabTip !== undefined){
31926             ti.setTooltip(panel.tabTip);
31927         }
31928         ti.on("activate", function(){
31929               this.setActivePanel(panel);
31930         }, this);
31931         if(this.config.closeOnTab){
31932             ti.on("beforeclose", function(t, e){
31933                 e.cancel = true;
31934                 this.remove(panel);
31935             }, this);
31936         }
31937         return ti;
31938     },
31939
31940     updatePanelTitle : function(panel, title){
31941         if(this.activePanel == panel){
31942             this.updateTitle(title);
31943         }
31944         if(this.tabs){
31945             var ti = this.tabs.getTab(panel.getEl().id);
31946             ti.setText(title);
31947             if(panel.tabTip !== undefined){
31948                 ti.setTooltip(panel.tabTip);
31949             }
31950         }
31951     },
31952
31953     updateTitle : function(title){
31954         if(this.titleTextEl && !this.config.title){
31955             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31956         }
31957     },
31958
31959     setActivePanel : function(panel){
31960         panel = this.getPanel(panel);
31961         if(this.activePanel && this.activePanel != panel){
31962             this.activePanel.setActiveState(false);
31963         }
31964         this.activePanel = panel;
31965         panel.setActiveState(true);
31966         if(this.panelSize){
31967             panel.setSize(this.panelSize.width, this.panelSize.height);
31968         }
31969         if(this.closeBtn){
31970             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31971         }
31972         this.updateTitle(panel.getTitle());
31973         if(this.tabs){
31974             this.fireEvent("invalidated", this);
31975         }
31976         this.fireEvent("panelactivated", this, panel);
31977     },
31978
31979     /**
31980      * Shows the specified panel.
31981      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31982      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31983      */
31984     showPanel : function(panel)
31985     {
31986         panel = this.getPanel(panel);
31987         if(panel){
31988             if(this.tabs){
31989                 var tab = this.tabs.getTab(panel.getEl().id);
31990                 if(tab.isHidden()){
31991                     this.tabs.unhideTab(tab.id);
31992                 }
31993                 tab.activate();
31994             }else{
31995                 this.setActivePanel(panel);
31996             }
31997         }
31998         return panel;
31999     },
32000
32001     /**
32002      * Get the active panel for this region.
32003      * @return {Roo.ContentPanel} The active panel or null
32004      */
32005     getActivePanel : function(){
32006         return this.activePanel;
32007     },
32008
32009     validateVisibility : function(){
32010         if(this.panels.getCount() < 1){
32011             this.updateTitle("&#160;");
32012             this.closeBtn.hide();
32013             this.hide();
32014         }else{
32015             if(!this.isVisible()){
32016                 this.show();
32017             }
32018         }
32019     },
32020
32021     /**
32022      * Adds the passed ContentPanel(s) to this region.
32023      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32024      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32025      */
32026     add : function(panel){
32027         if(arguments.length > 1){
32028             for(var i = 0, len = arguments.length; i < len; i++) {
32029                 this.add(arguments[i]);
32030             }
32031             return null;
32032         }
32033         if(this.hasPanel(panel)){
32034             this.showPanel(panel);
32035             return panel;
32036         }
32037         panel.setRegion(this);
32038         this.panels.add(panel);
32039         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32040             this.bodyEl.dom.appendChild(panel.getEl().dom);
32041             if(panel.background !== true){
32042                 this.setActivePanel(panel);
32043             }
32044             this.fireEvent("paneladded", this, panel);
32045             return panel;
32046         }
32047         if(!this.tabs){
32048             this.initTabs();
32049         }else{
32050             this.initPanelAsTab(panel);
32051         }
32052         if(panel.background !== true){
32053             this.tabs.activate(panel.getEl().id);
32054         }
32055         this.fireEvent("paneladded", this, panel);
32056         return panel;
32057     },
32058
32059     /**
32060      * Hides the tab for the specified panel.
32061      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32062      */
32063     hidePanel : function(panel){
32064         if(this.tabs && (panel = this.getPanel(panel))){
32065             this.tabs.hideTab(panel.getEl().id);
32066         }
32067     },
32068
32069     /**
32070      * Unhides the tab for a previously hidden panel.
32071      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32072      */
32073     unhidePanel : function(panel){
32074         if(this.tabs && (panel = this.getPanel(panel))){
32075             this.tabs.unhideTab(panel.getEl().id);
32076         }
32077     },
32078
32079     clearPanels : function(){
32080         while(this.panels.getCount() > 0){
32081              this.remove(this.panels.first());
32082         }
32083     },
32084
32085     /**
32086      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32087      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32088      * @param {Boolean} preservePanel Overrides the config preservePanel option
32089      * @return {Roo.ContentPanel} The panel that was removed
32090      */
32091     remove : function(panel, preservePanel){
32092         panel = this.getPanel(panel);
32093         if(!panel){
32094             return null;
32095         }
32096         var e = {};
32097         this.fireEvent("beforeremove", this, panel, e);
32098         if(e.cancel === true){
32099             return null;
32100         }
32101         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32102         var panelId = panel.getId();
32103         this.panels.removeKey(panelId);
32104         if(preservePanel){
32105             document.body.appendChild(panel.getEl().dom);
32106         }
32107         if(this.tabs){
32108             this.tabs.removeTab(panel.getEl().id);
32109         }else if (!preservePanel){
32110             this.bodyEl.dom.removeChild(panel.getEl().dom);
32111         }
32112         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32113             var p = this.panels.first();
32114             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32115             tempEl.appendChild(p.getEl().dom);
32116             this.bodyEl.update("");
32117             this.bodyEl.dom.appendChild(p.getEl().dom);
32118             tempEl = null;
32119             this.updateTitle(p.getTitle());
32120             this.tabs = null;
32121             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32122             this.setActivePanel(p);
32123         }
32124         panel.setRegion(null);
32125         if(this.activePanel == panel){
32126             this.activePanel = null;
32127         }
32128         if(this.config.autoDestroy !== false && preservePanel !== true){
32129             try{panel.destroy();}catch(e){}
32130         }
32131         this.fireEvent("panelremoved", this, panel);
32132         return panel;
32133     },
32134
32135     /**
32136      * Returns the TabPanel component used by this region
32137      * @return {Roo.TabPanel}
32138      */
32139     getTabs : function(){
32140         return this.tabs;
32141     },
32142
32143     createTool : function(parentEl, className){
32144         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32145             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32146         btn.addClassOnOver("x-layout-tools-button-over");
32147         return btn;
32148     }
32149 });/*
32150  * Based on:
32151  * Ext JS Library 1.1.1
32152  * Copyright(c) 2006-2007, Ext JS, LLC.
32153  *
32154  * Originally Released Under LGPL - original licence link has changed is not relivant.
32155  *
32156  * Fork - LGPL
32157  * <script type="text/javascript">
32158  */
32159  
32160
32161
32162 /**
32163  * @class Roo.SplitLayoutRegion
32164  * @extends Roo.LayoutRegion
32165  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32166  */
32167 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32168     this.cursor = cursor;
32169     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32170 };
32171
32172 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32173     splitTip : "Drag to resize.",
32174     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32175     useSplitTips : false,
32176
32177     applyConfig : function(config){
32178         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32179         if(config.split){
32180             if(!this.split){
32181                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32182                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32183                 /** The SplitBar for this region 
32184                 * @type Roo.SplitBar */
32185                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32186                 this.split.on("moved", this.onSplitMove, this);
32187                 this.split.useShim = config.useShim === true;
32188                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32189                 if(this.useSplitTips){
32190                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32191                 }
32192                 if(config.collapsible){
32193                     this.split.el.on("dblclick", this.collapse,  this);
32194                 }
32195             }
32196             if(typeof config.minSize != "undefined"){
32197                 this.split.minSize = config.minSize;
32198             }
32199             if(typeof config.maxSize != "undefined"){
32200                 this.split.maxSize = config.maxSize;
32201             }
32202             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32203                 this.hideSplitter();
32204             }
32205         }
32206     },
32207
32208     getHMaxSize : function(){
32209          var cmax = this.config.maxSize || 10000;
32210          var center = this.mgr.getRegion("center");
32211          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32212     },
32213
32214     getVMaxSize : function(){
32215          var cmax = this.config.maxSize || 10000;
32216          var center = this.mgr.getRegion("center");
32217          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32218     },
32219
32220     onSplitMove : function(split, newSize){
32221         this.fireEvent("resized", this, newSize);
32222     },
32223     
32224     /** 
32225      * Returns the {@link Roo.SplitBar} for this region.
32226      * @return {Roo.SplitBar}
32227      */
32228     getSplitBar : function(){
32229         return this.split;
32230     },
32231     
32232     hide : function(){
32233         this.hideSplitter();
32234         Roo.SplitLayoutRegion.superclass.hide.call(this);
32235     },
32236
32237     hideSplitter : function(){
32238         if(this.split){
32239             this.split.el.setLocation(-2000,-2000);
32240             this.split.el.hide();
32241         }
32242     },
32243
32244     show : function(){
32245         if(this.split){
32246             this.split.el.show();
32247         }
32248         Roo.SplitLayoutRegion.superclass.show.call(this);
32249     },
32250     
32251     beforeSlide: function(){
32252         if(Roo.isGecko){// firefox overflow auto bug workaround
32253             this.bodyEl.clip();
32254             if(this.tabs) {
32255                 this.tabs.bodyEl.clip();
32256             }
32257             if(this.activePanel){
32258                 this.activePanel.getEl().clip();
32259                 
32260                 if(this.activePanel.beforeSlide){
32261                     this.activePanel.beforeSlide();
32262                 }
32263             }
32264         }
32265     },
32266     
32267     afterSlide : function(){
32268         if(Roo.isGecko){// firefox overflow auto bug workaround
32269             this.bodyEl.unclip();
32270             if(this.tabs) {
32271                 this.tabs.bodyEl.unclip();
32272             }
32273             if(this.activePanel){
32274                 this.activePanel.getEl().unclip();
32275                 if(this.activePanel.afterSlide){
32276                     this.activePanel.afterSlide();
32277                 }
32278             }
32279         }
32280     },
32281
32282     initAutoHide : function(){
32283         if(this.autoHide !== false){
32284             if(!this.autoHideHd){
32285                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32286                 this.autoHideHd = {
32287                     "mouseout": function(e){
32288                         if(!e.within(this.el, true)){
32289                             st.delay(500);
32290                         }
32291                     },
32292                     "mouseover" : function(e){
32293                         st.cancel();
32294                     },
32295                     scope : this
32296                 };
32297             }
32298             this.el.on(this.autoHideHd);
32299         }
32300     },
32301
32302     clearAutoHide : function(){
32303         if(this.autoHide !== false){
32304             this.el.un("mouseout", this.autoHideHd.mouseout);
32305             this.el.un("mouseover", this.autoHideHd.mouseover);
32306         }
32307     },
32308
32309     clearMonitor : function(){
32310         Roo.get(document).un("click", this.slideInIf, this);
32311     },
32312
32313     // these names are backwards but not changed for compat
32314     slideOut : function(){
32315         if(this.isSlid || this.el.hasActiveFx()){
32316             return;
32317         }
32318         this.isSlid = true;
32319         if(this.collapseBtn){
32320             this.collapseBtn.hide();
32321         }
32322         this.closeBtnState = this.closeBtn.getStyle('display');
32323         this.closeBtn.hide();
32324         if(this.stickBtn){
32325             this.stickBtn.show();
32326         }
32327         this.el.show();
32328         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32329         this.beforeSlide();
32330         this.el.setStyle("z-index", 10001);
32331         this.el.slideIn(this.getSlideAnchor(), {
32332             callback: function(){
32333                 this.afterSlide();
32334                 this.initAutoHide();
32335                 Roo.get(document).on("click", this.slideInIf, this);
32336                 this.fireEvent("slideshow", this);
32337             },
32338             scope: this,
32339             block: true
32340         });
32341     },
32342
32343     afterSlideIn : function(){
32344         this.clearAutoHide();
32345         this.isSlid = false;
32346         this.clearMonitor();
32347         this.el.setStyle("z-index", "");
32348         if(this.collapseBtn){
32349             this.collapseBtn.show();
32350         }
32351         this.closeBtn.setStyle('display', this.closeBtnState);
32352         if(this.stickBtn){
32353             this.stickBtn.hide();
32354         }
32355         this.fireEvent("slidehide", this);
32356     },
32357
32358     slideIn : function(cb){
32359         if(!this.isSlid || this.el.hasActiveFx()){
32360             Roo.callback(cb);
32361             return;
32362         }
32363         this.isSlid = false;
32364         this.beforeSlide();
32365         this.el.slideOut(this.getSlideAnchor(), {
32366             callback: function(){
32367                 this.el.setLeftTop(-10000, -10000);
32368                 this.afterSlide();
32369                 this.afterSlideIn();
32370                 Roo.callback(cb);
32371             },
32372             scope: this,
32373             block: true
32374         });
32375     },
32376     
32377     slideInIf : function(e){
32378         if(!e.within(this.el)){
32379             this.slideIn();
32380         }
32381     },
32382
32383     animateCollapse : function(){
32384         this.beforeSlide();
32385         this.el.setStyle("z-index", 20000);
32386         var anchor = this.getSlideAnchor();
32387         this.el.slideOut(anchor, {
32388             callback : function(){
32389                 this.el.setStyle("z-index", "");
32390                 this.collapsedEl.slideIn(anchor, {duration:.3});
32391                 this.afterSlide();
32392                 this.el.setLocation(-10000,-10000);
32393                 this.el.hide();
32394                 this.fireEvent("collapsed", this);
32395             },
32396             scope: this,
32397             block: true
32398         });
32399     },
32400
32401     animateExpand : function(){
32402         this.beforeSlide();
32403         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32404         this.el.setStyle("z-index", 20000);
32405         this.collapsedEl.hide({
32406             duration:.1
32407         });
32408         this.el.slideIn(this.getSlideAnchor(), {
32409             callback : function(){
32410                 this.el.setStyle("z-index", "");
32411                 this.afterSlide();
32412                 if(this.split){
32413                     this.split.el.show();
32414                 }
32415                 this.fireEvent("invalidated", this);
32416                 this.fireEvent("expanded", this);
32417             },
32418             scope: this,
32419             block: true
32420         });
32421     },
32422
32423     anchors : {
32424         "west" : "left",
32425         "east" : "right",
32426         "north" : "top",
32427         "south" : "bottom"
32428     },
32429
32430     sanchors : {
32431         "west" : "l",
32432         "east" : "r",
32433         "north" : "t",
32434         "south" : "b"
32435     },
32436
32437     canchors : {
32438         "west" : "tl-tr",
32439         "east" : "tr-tl",
32440         "north" : "tl-bl",
32441         "south" : "bl-tl"
32442     },
32443
32444     getAnchor : function(){
32445         return this.anchors[this.position];
32446     },
32447
32448     getCollapseAnchor : function(){
32449         return this.canchors[this.position];
32450     },
32451
32452     getSlideAnchor : function(){
32453         return this.sanchors[this.position];
32454     },
32455
32456     getAlignAdj : function(){
32457         var cm = this.cmargins;
32458         switch(this.position){
32459             case "west":
32460                 return [0, 0];
32461             break;
32462             case "east":
32463                 return [0, 0];
32464             break;
32465             case "north":
32466                 return [0, 0];
32467             break;
32468             case "south":
32469                 return [0, 0];
32470             break;
32471         }
32472     },
32473
32474     getExpandAdj : function(){
32475         var c = this.collapsedEl, cm = this.cmargins;
32476         switch(this.position){
32477             case "west":
32478                 return [-(cm.right+c.getWidth()+cm.left), 0];
32479             break;
32480             case "east":
32481                 return [cm.right+c.getWidth()+cm.left, 0];
32482             break;
32483             case "north":
32484                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32485             break;
32486             case "south":
32487                 return [0, cm.top+cm.bottom+c.getHeight()];
32488             break;
32489         }
32490     }
32491 });/*
32492  * Based on:
32493  * Ext JS Library 1.1.1
32494  * Copyright(c) 2006-2007, Ext JS, LLC.
32495  *
32496  * Originally Released Under LGPL - original licence link has changed is not relivant.
32497  *
32498  * Fork - LGPL
32499  * <script type="text/javascript">
32500  */
32501 /*
32502  * These classes are private internal classes
32503  */
32504 Roo.CenterLayoutRegion = function(mgr, config){
32505     Roo.LayoutRegion.call(this, mgr, config, "center");
32506     this.visible = true;
32507     this.minWidth = config.minWidth || 20;
32508     this.minHeight = config.minHeight || 20;
32509 };
32510
32511 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32512     hide : function(){
32513         // center panel can't be hidden
32514     },
32515     
32516     show : function(){
32517         // center panel can't be hidden
32518     },
32519     
32520     getMinWidth: function(){
32521         return this.minWidth;
32522     },
32523     
32524     getMinHeight: function(){
32525         return this.minHeight;
32526     }
32527 });
32528
32529
32530 Roo.NorthLayoutRegion = function(mgr, config){
32531     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32532     if(this.split){
32533         this.split.placement = Roo.SplitBar.TOP;
32534         this.split.orientation = Roo.SplitBar.VERTICAL;
32535         this.split.el.addClass("x-layout-split-v");
32536     }
32537     var size = config.initialSize || config.height;
32538     if(typeof size != "undefined"){
32539         this.el.setHeight(size);
32540     }
32541 };
32542 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32543     orientation: Roo.SplitBar.VERTICAL,
32544     getBox : function(){
32545         if(this.collapsed){
32546             return this.collapsedEl.getBox();
32547         }
32548         var box = this.el.getBox();
32549         if(this.split){
32550             box.height += this.split.el.getHeight();
32551         }
32552         return box;
32553     },
32554     
32555     updateBox : function(box){
32556         if(this.split && !this.collapsed){
32557             box.height -= this.split.el.getHeight();
32558             this.split.el.setLeft(box.x);
32559             this.split.el.setTop(box.y+box.height);
32560             this.split.el.setWidth(box.width);
32561         }
32562         if(this.collapsed){
32563             this.updateBody(box.width, null);
32564         }
32565         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32566     }
32567 });
32568
32569 Roo.SouthLayoutRegion = function(mgr, config){
32570     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32571     if(this.split){
32572         this.split.placement = Roo.SplitBar.BOTTOM;
32573         this.split.orientation = Roo.SplitBar.VERTICAL;
32574         this.split.el.addClass("x-layout-split-v");
32575     }
32576     var size = config.initialSize || config.height;
32577     if(typeof size != "undefined"){
32578         this.el.setHeight(size);
32579     }
32580 };
32581 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32582     orientation: Roo.SplitBar.VERTICAL,
32583     getBox : function(){
32584         if(this.collapsed){
32585             return this.collapsedEl.getBox();
32586         }
32587         var box = this.el.getBox();
32588         if(this.split){
32589             var sh = this.split.el.getHeight();
32590             box.height += sh;
32591             box.y -= sh;
32592         }
32593         return box;
32594     },
32595     
32596     updateBox : function(box){
32597         if(this.split && !this.collapsed){
32598             var sh = this.split.el.getHeight();
32599             box.height -= sh;
32600             box.y += sh;
32601             this.split.el.setLeft(box.x);
32602             this.split.el.setTop(box.y-sh);
32603             this.split.el.setWidth(box.width);
32604         }
32605         if(this.collapsed){
32606             this.updateBody(box.width, null);
32607         }
32608         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32609     }
32610 });
32611
32612 Roo.EastLayoutRegion = function(mgr, config){
32613     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32614     if(this.split){
32615         this.split.placement = Roo.SplitBar.RIGHT;
32616         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32617         this.split.el.addClass("x-layout-split-h");
32618     }
32619     var size = config.initialSize || config.width;
32620     if(typeof size != "undefined"){
32621         this.el.setWidth(size);
32622     }
32623 };
32624 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32625     orientation: Roo.SplitBar.HORIZONTAL,
32626     getBox : function(){
32627         if(this.collapsed){
32628             return this.collapsedEl.getBox();
32629         }
32630         var box = this.el.getBox();
32631         if(this.split){
32632             var sw = this.split.el.getWidth();
32633             box.width += sw;
32634             box.x -= sw;
32635         }
32636         return box;
32637     },
32638
32639     updateBox : function(box){
32640         if(this.split && !this.collapsed){
32641             var sw = this.split.el.getWidth();
32642             box.width -= sw;
32643             this.split.el.setLeft(box.x);
32644             this.split.el.setTop(box.y);
32645             this.split.el.setHeight(box.height);
32646             box.x += sw;
32647         }
32648         if(this.collapsed){
32649             this.updateBody(null, box.height);
32650         }
32651         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32652     }
32653 });
32654
32655 Roo.WestLayoutRegion = function(mgr, config){
32656     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32657     if(this.split){
32658         this.split.placement = Roo.SplitBar.LEFT;
32659         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32660         this.split.el.addClass("x-layout-split-h");
32661     }
32662     var size = config.initialSize || config.width;
32663     if(typeof size != "undefined"){
32664         this.el.setWidth(size);
32665     }
32666 };
32667 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32668     orientation: Roo.SplitBar.HORIZONTAL,
32669     getBox : function(){
32670         if(this.collapsed){
32671             return this.collapsedEl.getBox();
32672         }
32673         var box = this.el.getBox();
32674         if(this.split){
32675             box.width += this.split.el.getWidth();
32676         }
32677         return box;
32678     },
32679     
32680     updateBox : function(box){
32681         if(this.split && !this.collapsed){
32682             var sw = this.split.el.getWidth();
32683             box.width -= sw;
32684             this.split.el.setLeft(box.x+box.width);
32685             this.split.el.setTop(box.y);
32686             this.split.el.setHeight(box.height);
32687         }
32688         if(this.collapsed){
32689             this.updateBody(null, box.height);
32690         }
32691         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32692     }
32693 });
32694 /*
32695  * Based on:
32696  * Ext JS Library 1.1.1
32697  * Copyright(c) 2006-2007, Ext JS, LLC.
32698  *
32699  * Originally Released Under LGPL - original licence link has changed is not relivant.
32700  *
32701  * Fork - LGPL
32702  * <script type="text/javascript">
32703  */
32704  
32705  
32706 /*
32707  * Private internal class for reading and applying state
32708  */
32709 Roo.LayoutStateManager = function(layout){
32710      // default empty state
32711      this.state = {
32712         north: {},
32713         south: {},
32714         east: {},
32715         west: {}       
32716     };
32717 };
32718
32719 Roo.LayoutStateManager.prototype = {
32720     init : function(layout, provider){
32721         this.provider = provider;
32722         var state = provider.get(layout.id+"-layout-state");
32723         if(state){
32724             var wasUpdating = layout.isUpdating();
32725             if(!wasUpdating){
32726                 layout.beginUpdate();
32727             }
32728             for(var key in state){
32729                 if(typeof state[key] != "function"){
32730                     var rstate = state[key];
32731                     var r = layout.getRegion(key);
32732                     if(r && rstate){
32733                         if(rstate.size){
32734                             r.resizeTo(rstate.size);
32735                         }
32736                         if(rstate.collapsed == true){
32737                             r.collapse(true);
32738                         }else{
32739                             r.expand(null, true);
32740                         }
32741                     }
32742                 }
32743             }
32744             if(!wasUpdating){
32745                 layout.endUpdate();
32746             }
32747             this.state = state; 
32748         }
32749         this.layout = layout;
32750         layout.on("regionresized", this.onRegionResized, this);
32751         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32752         layout.on("regionexpanded", this.onRegionExpanded, this);
32753     },
32754     
32755     storeState : function(){
32756         this.provider.set(this.layout.id+"-layout-state", this.state);
32757     },
32758     
32759     onRegionResized : function(region, newSize){
32760         this.state[region.getPosition()].size = newSize;
32761         this.storeState();
32762     },
32763     
32764     onRegionCollapsed : function(region){
32765         this.state[region.getPosition()].collapsed = true;
32766         this.storeState();
32767     },
32768     
32769     onRegionExpanded : function(region){
32770         this.state[region.getPosition()].collapsed = false;
32771         this.storeState();
32772     }
32773 };/*
32774  * Based on:
32775  * Ext JS Library 1.1.1
32776  * Copyright(c) 2006-2007, Ext JS, LLC.
32777  *
32778  * Originally Released Under LGPL - original licence link has changed is not relivant.
32779  *
32780  * Fork - LGPL
32781  * <script type="text/javascript">
32782  */
32783 /**
32784  * @class Roo.ContentPanel
32785  * @extends Roo.util.Observable
32786  * @children Roo.form.Form Roo.JsonView Roo.View
32787  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
32788  * A basic ContentPanel element.
32789  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32790  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32791  * @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
32792  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32793  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32794  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32795  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
32796  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32797  * @cfg {String} title          The title for this panel
32798  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32799  * @cfg {String} url            Calls {@link #setUrl} with this value
32800  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32801  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32802  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32803  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32804  * @cfg {String}    style  Extra style to add to the content panel
32805  * @cfg {Roo.menu.Menu} menu  popup menu
32806
32807  * @constructor
32808  * Create a new ContentPanel.
32809  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32810  * @param {String/Object} config A string to set only the title or a config object
32811  * @param {String} content (optional) Set the HTML content for this panel
32812  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32813  */
32814 Roo.ContentPanel = function(el, config, content){
32815     
32816      
32817     /*
32818     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32819         config = el;
32820         el = Roo.id();
32821     }
32822     if (config && config.parentLayout) { 
32823         el = config.parentLayout.el.createChild(); 
32824     }
32825     */
32826     if(el.autoCreate){ // xtype is available if this is called from factory
32827         config = el;
32828         el = Roo.id();
32829     }
32830     this.el = Roo.get(el);
32831     if(!this.el && config && config.autoCreate){
32832         if(typeof config.autoCreate == "object"){
32833             if(!config.autoCreate.id){
32834                 config.autoCreate.id = config.id||el;
32835             }
32836             this.el = Roo.DomHelper.append(document.body,
32837                         config.autoCreate, true);
32838         }else{
32839             this.el = Roo.DomHelper.append(document.body,
32840                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32841         }
32842     }
32843     
32844     
32845     this.closable = false;
32846     this.loaded = false;
32847     this.active = false;
32848     if(typeof config == "string"){
32849         this.title = config;
32850     }else{
32851         Roo.apply(this, config);
32852     }
32853     
32854     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32855         this.wrapEl = this.el.wrap();
32856         this.toolbar.container = this.el.insertSibling(false, 'before');
32857         this.toolbar = new Roo.Toolbar(this.toolbar);
32858     }
32859     
32860     // xtype created footer. - not sure if will work as we normally have to render first..
32861     if (this.footer && !this.footer.el && this.footer.xtype) {
32862         if (!this.wrapEl) {
32863             this.wrapEl = this.el.wrap();
32864         }
32865     
32866         this.footer.container = this.wrapEl.createChild();
32867          
32868         this.footer = Roo.factory(this.footer, Roo);
32869         
32870     }
32871     
32872     if(this.resizeEl){
32873         this.resizeEl = Roo.get(this.resizeEl, true);
32874     }else{
32875         this.resizeEl = this.el;
32876     }
32877     // handle view.xtype
32878     
32879  
32880     
32881     
32882     this.addEvents({
32883         /**
32884          * @event activate
32885          * Fires when this panel is activated. 
32886          * @param {Roo.ContentPanel} this
32887          */
32888         "activate" : true,
32889         /**
32890          * @event deactivate
32891          * Fires when this panel is activated. 
32892          * @param {Roo.ContentPanel} this
32893          */
32894         "deactivate" : true,
32895
32896         /**
32897          * @event resize
32898          * Fires when this panel is resized if fitToFrame is true.
32899          * @param {Roo.ContentPanel} this
32900          * @param {Number} width The width after any component adjustments
32901          * @param {Number} height The height after any component adjustments
32902          */
32903         "resize" : true,
32904         
32905          /**
32906          * @event render
32907          * Fires when this tab is created
32908          * @param {Roo.ContentPanel} this
32909          */
32910         "render" : true
32911          
32912         
32913     });
32914     
32915
32916     
32917     
32918     if(this.autoScroll){
32919         this.resizeEl.setStyle("overflow", "auto");
32920     } else {
32921         // fix randome scrolling
32922         this.el.on('scroll', function() {
32923             Roo.log('fix random scolling');
32924             this.scrollTo('top',0); 
32925         });
32926     }
32927     content = content || this.content;
32928     if(content){
32929         this.setContent(content);
32930     }
32931     if(config && config.url){
32932         this.setUrl(this.url, this.params, this.loadOnce);
32933     }
32934     
32935     
32936     
32937     Roo.ContentPanel.superclass.constructor.call(this);
32938     
32939     if (this.view && typeof(this.view.xtype) != 'undefined') {
32940         this.view.el = this.el.appendChild(document.createElement("div"));
32941         this.view = Roo.factory(this.view); 
32942         this.view.render  &&  this.view.render(false, '');  
32943     }
32944     
32945     
32946     this.fireEvent('render', this);
32947 };
32948
32949 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32950     tabTip:'',
32951     setRegion : function(region){
32952         this.region = region;
32953         if(region){
32954            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32955         }else{
32956            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32957         } 
32958     },
32959     
32960     /**
32961      * Returns the toolbar for this Panel if one was configured. 
32962      * @return {Roo.Toolbar} 
32963      */
32964     getToolbar : function(){
32965         return this.toolbar;
32966     },
32967     
32968     setActiveState : function(active){
32969         this.active = active;
32970         if(!active){
32971             this.fireEvent("deactivate", this);
32972         }else{
32973             this.fireEvent("activate", this);
32974         }
32975     },
32976     /**
32977      * Updates this panel's element
32978      * @param {String} content The new content
32979      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32980     */
32981     setContent : function(content, loadScripts){
32982         this.el.update(content, loadScripts);
32983     },
32984
32985     ignoreResize : function(w, h){
32986         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32987             return true;
32988         }else{
32989             this.lastSize = {width: w, height: h};
32990             return false;
32991         }
32992     },
32993     /**
32994      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32995      * @return {Roo.UpdateManager} The UpdateManager
32996      */
32997     getUpdateManager : function(){
32998         return this.el.getUpdateManager();
32999     },
33000      /**
33001      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33002      * @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:
33003 <pre><code>
33004 panel.load({
33005     url: "your-url.php",
33006     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33007     callback: yourFunction,
33008     scope: yourObject, //(optional scope)
33009     discardUrl: false,
33010     nocache: false,
33011     text: "Loading...",
33012     timeout: 30,
33013     scripts: false
33014 });
33015 </code></pre>
33016      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33017      * 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.
33018      * @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}
33019      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33020      * @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.
33021      * @return {Roo.ContentPanel} this
33022      */
33023     load : function(){
33024         var um = this.el.getUpdateManager();
33025         um.update.apply(um, arguments);
33026         return this;
33027     },
33028
33029
33030     /**
33031      * 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.
33032      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33033      * @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)
33034      * @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)
33035      * @return {Roo.UpdateManager} The UpdateManager
33036      */
33037     setUrl : function(url, params, loadOnce){
33038         if(this.refreshDelegate){
33039             this.removeListener("activate", this.refreshDelegate);
33040         }
33041         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33042         this.on("activate", this.refreshDelegate);
33043         return this.el.getUpdateManager();
33044     },
33045     
33046     _handleRefresh : function(url, params, loadOnce){
33047         if(!loadOnce || !this.loaded){
33048             var updater = this.el.getUpdateManager();
33049             updater.update(url, params, this._setLoaded.createDelegate(this));
33050         }
33051     },
33052     
33053     _setLoaded : function(){
33054         this.loaded = true;
33055     }, 
33056     
33057     /**
33058      * Returns this panel's id
33059      * @return {String} 
33060      */
33061     getId : function(){
33062         return this.el.id;
33063     },
33064     
33065     /** 
33066      * Returns this panel's element - used by regiosn to add.
33067      * @return {Roo.Element} 
33068      */
33069     getEl : function(){
33070         return this.wrapEl || this.el;
33071     },
33072     
33073     adjustForComponents : function(width, height)
33074     {
33075         //Roo.log('adjustForComponents ');
33076         if(this.resizeEl != this.el){
33077             width -= this.el.getFrameWidth('lr');
33078             height -= this.el.getFrameWidth('tb');
33079         }
33080         if(this.toolbar){
33081             var te = this.toolbar.getEl();
33082             height -= te.getHeight();
33083             te.setWidth(width);
33084         }
33085         if(this.footer){
33086             var te = this.footer.getEl();
33087             //Roo.log("footer:" + te.getHeight());
33088             
33089             height -= te.getHeight();
33090             te.setWidth(width);
33091         }
33092         
33093         
33094         if(this.adjustments){
33095             width += this.adjustments[0];
33096             height += this.adjustments[1];
33097         }
33098         return {"width": width, "height": height};
33099     },
33100     
33101     setSize : function(width, height){
33102         if(this.fitToFrame && !this.ignoreResize(width, height)){
33103             if(this.fitContainer && this.resizeEl != this.el){
33104                 this.el.setSize(width, height);
33105             }
33106             var size = this.adjustForComponents(width, height);
33107             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33108             this.fireEvent('resize', this, size.width, size.height);
33109         }
33110     },
33111     
33112     /**
33113      * Returns this panel's title
33114      * @return {String} 
33115      */
33116     getTitle : function(){
33117         return this.title;
33118     },
33119     
33120     /**
33121      * Set this panel's title
33122      * @param {String} title
33123      */
33124     setTitle : function(title){
33125         this.title = title;
33126         if(this.region){
33127             this.region.updatePanelTitle(this, title);
33128         }
33129     },
33130     
33131     /**
33132      * Returns true is this panel was configured to be closable
33133      * @return {Boolean} 
33134      */
33135     isClosable : function(){
33136         return this.closable;
33137     },
33138     
33139     beforeSlide : function(){
33140         this.el.clip();
33141         this.resizeEl.clip();
33142     },
33143     
33144     afterSlide : function(){
33145         this.el.unclip();
33146         this.resizeEl.unclip();
33147     },
33148     
33149     /**
33150      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33151      *   Will fail silently if the {@link #setUrl} method has not been called.
33152      *   This does not activate the panel, just updates its content.
33153      */
33154     refresh : function(){
33155         if(this.refreshDelegate){
33156            this.loaded = false;
33157            this.refreshDelegate();
33158         }
33159     },
33160     
33161     /**
33162      * Destroys this panel
33163      */
33164     destroy : function(){
33165         this.el.removeAllListeners();
33166         var tempEl = document.createElement("span");
33167         tempEl.appendChild(this.el.dom);
33168         tempEl.innerHTML = "";
33169         this.el.remove();
33170         this.el = null;
33171     },
33172     
33173     /**
33174      * form - if the content panel contains a form - this is a reference to it.
33175      * @type {Roo.form.Form}
33176      */
33177     form : false,
33178     /**
33179      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33180      *    This contains a reference to it.
33181      * @type {Roo.View}
33182      */
33183     view : false,
33184     
33185       /**
33186      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33187      * <pre><code>
33188
33189 layout.addxtype({
33190        xtype : 'Form',
33191        items: [ .... ]
33192    }
33193 );
33194
33195 </code></pre>
33196      * @param {Object} cfg Xtype definition of item to add.
33197      */
33198     
33199     addxtype : function(cfg) {
33200         // add form..
33201         if (cfg.xtype.match(/^Form$/)) {
33202             
33203             var el;
33204             //if (this.footer) {
33205             //    el = this.footer.container.insertSibling(false, 'before');
33206             //} else {
33207                 el = this.el.createChild();
33208             //}
33209
33210             this.form = new  Roo.form.Form(cfg);
33211             
33212             
33213             if ( this.form.allItems.length) {
33214                 this.form.render(el.dom);
33215             }
33216             return this.form;
33217         }
33218         // should only have one of theses..
33219         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33220             // views.. should not be just added - used named prop 'view''
33221             
33222             cfg.el = this.el.appendChild(document.createElement("div"));
33223             // factory?
33224             
33225             var ret = new Roo.factory(cfg);
33226              
33227              ret.render && ret.render(false, ''); // render blank..
33228             this.view = ret;
33229             return ret;
33230         }
33231         return false;
33232     }
33233 });
33234
33235 /**
33236  * @class Roo.GridPanel
33237  * @extends Roo.ContentPanel
33238  * @constructor
33239  * Create a new GridPanel.
33240  * @param {Roo.grid.Grid} grid The grid for this panel
33241  * @param {String/Object} config A string to set only the panel's title, or a config object
33242  */
33243 Roo.GridPanel = function(grid, config){
33244     
33245   
33246     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33247         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33248         
33249     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33250     
33251     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33252     
33253     if(this.toolbar){
33254         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33255     }
33256     // xtype created footer. - not sure if will work as we normally have to render first..
33257     if (this.footer && !this.footer.el && this.footer.xtype) {
33258         
33259         this.footer.container = this.grid.getView().getFooterPanel(true);
33260         this.footer.dataSource = this.grid.dataSource;
33261         this.footer = Roo.factory(this.footer, Roo);
33262         
33263     }
33264     
33265     grid.monitorWindowResize = false; // turn off autosizing
33266     grid.autoHeight = false;
33267     grid.autoWidth = false;
33268     this.grid = grid;
33269     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33270 };
33271
33272 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33273     getId : function(){
33274         return this.grid.id;
33275     },
33276     
33277     /**
33278      * Returns the grid for this panel
33279      * @return {Roo.grid.Grid} 
33280      */
33281     getGrid : function(){
33282         return this.grid;    
33283     },
33284     
33285     setSize : function(width, height){
33286         if(!this.ignoreResize(width, height)){
33287             var grid = this.grid;
33288             var size = this.adjustForComponents(width, height);
33289             grid.getGridEl().setSize(size.width, size.height);
33290             grid.autoSize();
33291         }
33292     },
33293     
33294     beforeSlide : function(){
33295         this.grid.getView().scroller.clip();
33296     },
33297     
33298     afterSlide : function(){
33299         this.grid.getView().scroller.unclip();
33300     },
33301     
33302     destroy : function(){
33303         this.grid.destroy();
33304         delete this.grid;
33305         Roo.GridPanel.superclass.destroy.call(this); 
33306     }
33307 });
33308
33309
33310 /**
33311  * @class Roo.NestedLayoutPanel
33312  * @extends Roo.ContentPanel
33313  * @constructor
33314  * Create a new NestedLayoutPanel.
33315  * 
33316  * 
33317  * @param {Roo.BorderLayout} layout [required] The layout for this panel
33318  * @param {String/Object} config A string to set only the title or a config object
33319  */
33320 Roo.NestedLayoutPanel = function(layout, config)
33321 {
33322     // construct with only one argument..
33323     /* FIXME - implement nicer consturctors
33324     if (layout.layout) {
33325         config = layout;
33326         layout = config.layout;
33327         delete config.layout;
33328     }
33329     if (layout.xtype && !layout.getEl) {
33330         // then layout needs constructing..
33331         layout = Roo.factory(layout, Roo);
33332     }
33333     */
33334     
33335     
33336     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33337     
33338     layout.monitorWindowResize = false; // turn off autosizing
33339     this.layout = layout;
33340     this.layout.getEl().addClass("x-layout-nested-layout");
33341     
33342     
33343     
33344     
33345 };
33346
33347 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33348
33349     setSize : function(width, height){
33350         if(!this.ignoreResize(width, height)){
33351             var size = this.adjustForComponents(width, height);
33352             var el = this.layout.getEl();
33353             el.setSize(size.width, size.height);
33354             var touch = el.dom.offsetWidth;
33355             this.layout.layout();
33356             // ie requires a double layout on the first pass
33357             if(Roo.isIE && !this.initialized){
33358                 this.initialized = true;
33359                 this.layout.layout();
33360             }
33361         }
33362     },
33363     
33364     // activate all subpanels if not currently active..
33365     
33366     setActiveState : function(active){
33367         this.active = active;
33368         if(!active){
33369             this.fireEvent("deactivate", this);
33370             return;
33371         }
33372         
33373         this.fireEvent("activate", this);
33374         // not sure if this should happen before or after..
33375         if (!this.layout) {
33376             return; // should not happen..
33377         }
33378         var reg = false;
33379         for (var r in this.layout.regions) {
33380             reg = this.layout.getRegion(r);
33381             if (reg.getActivePanel()) {
33382                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33383                 reg.setActivePanel(reg.getActivePanel());
33384                 continue;
33385             }
33386             if (!reg.panels.length) {
33387                 continue;
33388             }
33389             reg.showPanel(reg.getPanel(0));
33390         }
33391         
33392         
33393         
33394         
33395     },
33396     
33397     /**
33398      * Returns the nested BorderLayout for this panel
33399      * @return {Roo.BorderLayout} 
33400      */
33401     getLayout : function(){
33402         return this.layout;
33403     },
33404     
33405      /**
33406      * Adds a xtype elements to the layout of the nested panel
33407      * <pre><code>
33408
33409 panel.addxtype({
33410        xtype : 'ContentPanel',
33411        region: 'west',
33412        items: [ .... ]
33413    }
33414 );
33415
33416 panel.addxtype({
33417         xtype : 'NestedLayoutPanel',
33418         region: 'west',
33419         layout: {
33420            center: { },
33421            west: { }   
33422         },
33423         items : [ ... list of content panels or nested layout panels.. ]
33424    }
33425 );
33426 </code></pre>
33427      * @param {Object} cfg Xtype definition of item to add.
33428      */
33429     addxtype : function(cfg) {
33430         return this.layout.addxtype(cfg);
33431     
33432     }
33433 });
33434
33435 Roo.ScrollPanel = function(el, config, content){
33436     config = config || {};
33437     config.fitToFrame = true;
33438     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33439     
33440     this.el.dom.style.overflow = "hidden";
33441     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33442     this.el.removeClass("x-layout-inactive-content");
33443     this.el.on("mousewheel", this.onWheel, this);
33444
33445     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33446     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33447     up.unselectable(); down.unselectable();
33448     up.on("click", this.scrollUp, this);
33449     down.on("click", this.scrollDown, this);
33450     up.addClassOnOver("x-scroller-btn-over");
33451     down.addClassOnOver("x-scroller-btn-over");
33452     up.addClassOnClick("x-scroller-btn-click");
33453     down.addClassOnClick("x-scroller-btn-click");
33454     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33455
33456     this.resizeEl = this.el;
33457     this.el = wrap; this.up = up; this.down = down;
33458 };
33459
33460 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33461     increment : 100,
33462     wheelIncrement : 5,
33463     scrollUp : function(){
33464         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33465     },
33466
33467     scrollDown : function(){
33468         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33469     },
33470
33471     afterScroll : function(){
33472         var el = this.resizeEl;
33473         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33474         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33475         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33476     },
33477
33478     setSize : function(){
33479         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33480         this.afterScroll();
33481     },
33482
33483     onWheel : function(e){
33484         var d = e.getWheelDelta();
33485         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33486         this.afterScroll();
33487         e.stopEvent();
33488     },
33489
33490     setContent : function(content, loadScripts){
33491         this.resizeEl.update(content, loadScripts);
33492     }
33493
33494 });
33495
33496
33497
33498 /**
33499  * @class Roo.TreePanel
33500  * @extends Roo.ContentPanel
33501  * Treepanel component
33502  * 
33503  * @constructor
33504  * Create a new TreePanel. - defaults to fit/scoll contents.
33505  * @param {String/Object} config A string to set only the panel's title, or a config object
33506  */
33507 Roo.TreePanel = function(config){
33508     var el = config.el;
33509     var tree = config.tree;
33510     delete config.tree; 
33511     delete config.el; // hopefull!
33512     
33513     // wrapper for IE7 strict & safari scroll issue
33514     
33515     var treeEl = el.createChild();
33516     config.resizeEl = treeEl;
33517     
33518     
33519     
33520     Roo.TreePanel.superclass.constructor.call(this, el, config);
33521  
33522  
33523     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33524     //console.log(tree);
33525     this.on('activate', function()
33526     {
33527         if (this.tree.rendered) {
33528             return;
33529         }
33530         //console.log('render tree');
33531         this.tree.render();
33532     });
33533     // this should not be needed.. - it's actually the 'el' that resizes?
33534     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33535     
33536     //this.on('resize',  function (cp, w, h) {
33537     //        this.tree.innerCt.setWidth(w);
33538     //        this.tree.innerCt.setHeight(h);
33539     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33540     //});
33541
33542         
33543     
33544 };
33545
33546 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33547     fitToFrame : true,
33548     autoScroll : true,
33549     /*
33550      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
33551      */
33552     tree : false
33553
33554 });
33555
33556
33557
33558
33559
33560
33561
33562
33563
33564
33565
33566 /*
33567  * Based on:
33568  * Ext JS Library 1.1.1
33569  * Copyright(c) 2006-2007, Ext JS, LLC.
33570  *
33571  * Originally Released Under LGPL - original licence link has changed is not relivant.
33572  *
33573  * Fork - LGPL
33574  * <script type="text/javascript">
33575  */
33576  
33577
33578 /**
33579  * @class Roo.ReaderLayout
33580  * @extends Roo.BorderLayout
33581  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33582  * center region containing two nested regions (a top one for a list view and one for item preview below),
33583  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33584  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33585  * expedites the setup of the overall layout and regions for this common application style.
33586  * Example:
33587  <pre><code>
33588 var reader = new Roo.ReaderLayout();
33589 var CP = Roo.ContentPanel;  // shortcut for adding
33590
33591 reader.beginUpdate();
33592 reader.add("north", new CP("north", "North"));
33593 reader.add("west", new CP("west", {title: "West"}));
33594 reader.add("east", new CP("east", {title: "East"}));
33595
33596 reader.regions.listView.add(new CP("listView", "List"));
33597 reader.regions.preview.add(new CP("preview", "Preview"));
33598 reader.endUpdate();
33599 </code></pre>
33600 * @constructor
33601 * Create a new ReaderLayout
33602 * @param {Object} config Configuration options
33603 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33604 * document.body if omitted)
33605 */
33606 Roo.ReaderLayout = function(config, renderTo){
33607     var c = config || {size:{}};
33608     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33609         north: c.north !== false ? Roo.apply({
33610             split:false,
33611             initialSize: 32,
33612             titlebar: false
33613         }, c.north) : false,
33614         west: c.west !== false ? Roo.apply({
33615             split:true,
33616             initialSize: 200,
33617             minSize: 175,
33618             maxSize: 400,
33619             titlebar: true,
33620             collapsible: true,
33621             animate: true,
33622             margins:{left:5,right:0,bottom:5,top:5},
33623             cmargins:{left:5,right:5,bottom:5,top:5}
33624         }, c.west) : false,
33625         east: c.east !== false ? Roo.apply({
33626             split:true,
33627             initialSize: 200,
33628             minSize: 175,
33629             maxSize: 400,
33630             titlebar: true,
33631             collapsible: true,
33632             animate: true,
33633             margins:{left:0,right:5,bottom:5,top:5},
33634             cmargins:{left:5,right:5,bottom:5,top:5}
33635         }, c.east) : false,
33636         center: Roo.apply({
33637             tabPosition: 'top',
33638             autoScroll:false,
33639             closeOnTab: true,
33640             titlebar:false,
33641             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33642         }, c.center)
33643     });
33644
33645     this.el.addClass('x-reader');
33646
33647     this.beginUpdate();
33648
33649     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33650         south: c.preview !== false ? Roo.apply({
33651             split:true,
33652             initialSize: 200,
33653             minSize: 100,
33654             autoScroll:true,
33655             collapsible:true,
33656             titlebar: true,
33657             cmargins:{top:5,left:0, right:0, bottom:0}
33658         }, c.preview) : false,
33659         center: Roo.apply({
33660             autoScroll:false,
33661             titlebar:false,
33662             minHeight:200
33663         }, c.listView)
33664     });
33665     this.add('center', new Roo.NestedLayoutPanel(inner,
33666             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33667
33668     this.endUpdate();
33669
33670     this.regions.preview = inner.getRegion('south');
33671     this.regions.listView = inner.getRegion('center');
33672 };
33673
33674 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33675  * Based on:
33676  * Ext JS Library 1.1.1
33677  * Copyright(c) 2006-2007, Ext JS, LLC.
33678  *
33679  * Originally Released Under LGPL - original licence link has changed is not relivant.
33680  *
33681  * Fork - LGPL
33682  * <script type="text/javascript">
33683  */
33684  
33685 /**
33686  * @class Roo.grid.Grid
33687  * @extends Roo.util.Observable
33688  * This class represents the primary interface of a component based grid control.
33689  * <br><br>Usage:<pre><code>
33690  var grid = new Roo.grid.Grid("my-container-id", {
33691      ds: myDataStore,
33692      cm: myColModel,
33693      selModel: mySelectionModel,
33694      autoSizeColumns: true,
33695      monitorWindowResize: false,
33696      trackMouseOver: true
33697  });
33698  // set any options
33699  grid.render();
33700  * </code></pre>
33701  * <b>Common Problems:</b><br/>
33702  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33703  * element will correct this<br/>
33704  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33705  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33706  * are unpredictable.<br/>
33707  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33708  * grid to calculate dimensions/offsets.<br/>
33709   * @constructor
33710  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33711  * The container MUST have some type of size defined for the grid to fill. The container will be
33712  * automatically set to position relative if it isn't already.
33713  * @param {Object} config A config object that sets properties on this grid.
33714  */
33715 Roo.grid.Grid = function(container, config){
33716         // initialize the container
33717         this.container = Roo.get(container);
33718         this.container.update("");
33719         this.container.setStyle("overflow", "hidden");
33720     this.container.addClass('x-grid-container');
33721
33722     this.id = this.container.id;
33723
33724     Roo.apply(this, config);
33725     // check and correct shorthanded configs
33726     if(this.ds){
33727         this.dataSource = this.ds;
33728         delete this.ds;
33729     }
33730     if(this.cm){
33731         this.colModel = this.cm;
33732         delete this.cm;
33733     }
33734     if(this.sm){
33735         this.selModel = this.sm;
33736         delete this.sm;
33737     }
33738
33739     if (this.selModel) {
33740         this.selModel = Roo.factory(this.selModel, Roo.grid);
33741         this.sm = this.selModel;
33742         this.sm.xmodule = this.xmodule || false;
33743     }
33744     if (typeof(this.colModel.config) == 'undefined') {
33745         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33746         this.cm = this.colModel;
33747         this.cm.xmodule = this.xmodule || false;
33748     }
33749     if (this.dataSource) {
33750         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33751         this.ds = this.dataSource;
33752         this.ds.xmodule = this.xmodule || false;
33753          
33754     }
33755     
33756     
33757     
33758     if(this.width){
33759         this.container.setWidth(this.width);
33760     }
33761
33762     if(this.height){
33763         this.container.setHeight(this.height);
33764     }
33765     /** @private */
33766         this.addEvents({
33767         // raw events
33768         /**
33769          * @event click
33770          * The raw click event for the entire grid.
33771          * @param {Roo.EventObject} e
33772          */
33773         "click" : true,
33774         /**
33775          * @event dblclick
33776          * The raw dblclick event for the entire grid.
33777          * @param {Roo.EventObject} e
33778          */
33779         "dblclick" : true,
33780         /**
33781          * @event contextmenu
33782          * The raw contextmenu event for the entire grid.
33783          * @param {Roo.EventObject} e
33784          */
33785         "contextmenu" : true,
33786         /**
33787          * @event mousedown
33788          * The raw mousedown event for the entire grid.
33789          * @param {Roo.EventObject} e
33790          */
33791         "mousedown" : true,
33792         /**
33793          * @event mouseup
33794          * The raw mouseup event for the entire grid.
33795          * @param {Roo.EventObject} e
33796          */
33797         "mouseup" : true,
33798         /**
33799          * @event mouseover
33800          * The raw mouseover event for the entire grid.
33801          * @param {Roo.EventObject} e
33802          */
33803         "mouseover" : true,
33804         /**
33805          * @event mouseout
33806          * The raw mouseout event for the entire grid.
33807          * @param {Roo.EventObject} e
33808          */
33809         "mouseout" : true,
33810         /**
33811          * @event keypress
33812          * The raw keypress event for the entire grid.
33813          * @param {Roo.EventObject} e
33814          */
33815         "keypress" : true,
33816         /**
33817          * @event keydown
33818          * The raw keydown event for the entire grid.
33819          * @param {Roo.EventObject} e
33820          */
33821         "keydown" : true,
33822
33823         // custom events
33824
33825         /**
33826          * @event cellclick
33827          * Fires when a cell is clicked
33828          * @param {Grid} this
33829          * @param {Number} rowIndex
33830          * @param {Number} columnIndex
33831          * @param {Roo.EventObject} e
33832          */
33833         "cellclick" : true,
33834         /**
33835          * @event celldblclick
33836          * Fires when a cell is double clicked
33837          * @param {Grid} this
33838          * @param {Number} rowIndex
33839          * @param {Number} columnIndex
33840          * @param {Roo.EventObject} e
33841          */
33842         "celldblclick" : true,
33843         /**
33844          * @event rowclick
33845          * Fires when a row is clicked
33846          * @param {Grid} this
33847          * @param {Number} rowIndex
33848          * @param {Roo.EventObject} e
33849          */
33850         "rowclick" : true,
33851         /**
33852          * @event rowdblclick
33853          * Fires when a row is double clicked
33854          * @param {Grid} this
33855          * @param {Number} rowIndex
33856          * @param {Roo.EventObject} e
33857          */
33858         "rowdblclick" : true,
33859         /**
33860          * @event headerclick
33861          * Fires when a header is clicked
33862          * @param {Grid} this
33863          * @param {Number} columnIndex
33864          * @param {Roo.EventObject} e
33865          */
33866         "headerclick" : true,
33867         /**
33868          * @event headerdblclick
33869          * Fires when a header cell is double clicked
33870          * @param {Grid} this
33871          * @param {Number} columnIndex
33872          * @param {Roo.EventObject} e
33873          */
33874         "headerdblclick" : true,
33875         /**
33876          * @event rowcontextmenu
33877          * Fires when a row is right clicked
33878          * @param {Grid} this
33879          * @param {Number} rowIndex
33880          * @param {Roo.EventObject} e
33881          */
33882         "rowcontextmenu" : true,
33883         /**
33884          * @event cellcontextmenu
33885          * Fires when a cell is right clicked
33886          * @param {Grid} this
33887          * @param {Number} rowIndex
33888          * @param {Number} cellIndex
33889          * @param {Roo.EventObject} e
33890          */
33891          "cellcontextmenu" : true,
33892         /**
33893          * @event headercontextmenu
33894          * Fires when a header is right clicked
33895          * @param {Grid} this
33896          * @param {Number} columnIndex
33897          * @param {Roo.EventObject} e
33898          */
33899         "headercontextmenu" : true,
33900         /**
33901          * @event bodyscroll
33902          * Fires when the body element is scrolled
33903          * @param {Number} scrollLeft
33904          * @param {Number} scrollTop
33905          */
33906         "bodyscroll" : true,
33907         /**
33908          * @event columnresize
33909          * Fires when the user resizes a column
33910          * @param {Number} columnIndex
33911          * @param {Number} newSize
33912          */
33913         "columnresize" : true,
33914         /**
33915          * @event columnmove
33916          * Fires when the user moves a column
33917          * @param {Number} oldIndex
33918          * @param {Number} newIndex
33919          */
33920         "columnmove" : true,
33921         /**
33922          * @event startdrag
33923          * Fires when row(s) start being dragged
33924          * @param {Grid} this
33925          * @param {Roo.GridDD} dd The drag drop object
33926          * @param {event} e The raw browser event
33927          */
33928         "startdrag" : true,
33929         /**
33930          * @event enddrag
33931          * Fires when a drag operation is complete
33932          * @param {Grid} this
33933          * @param {Roo.GridDD} dd The drag drop object
33934          * @param {event} e The raw browser event
33935          */
33936         "enddrag" : true,
33937         /**
33938          * @event dragdrop
33939          * Fires when dragged row(s) are dropped on a valid DD target
33940          * @param {Grid} this
33941          * @param {Roo.GridDD} dd The drag drop object
33942          * @param {String} targetId The target drag drop object
33943          * @param {event} e The raw browser event
33944          */
33945         "dragdrop" : true,
33946         /**
33947          * @event dragover
33948          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33949          * @param {Grid} this
33950          * @param {Roo.GridDD} dd The drag drop object
33951          * @param {String} targetId The target drag drop object
33952          * @param {event} e The raw browser event
33953          */
33954         "dragover" : true,
33955         /**
33956          * @event dragenter
33957          *  Fires when the dragged row(s) first cross another DD target while being dragged
33958          * @param {Grid} this
33959          * @param {Roo.GridDD} dd The drag drop object
33960          * @param {String} targetId The target drag drop object
33961          * @param {event} e The raw browser event
33962          */
33963         "dragenter" : true,
33964         /**
33965          * @event dragout
33966          * Fires when the dragged row(s) leave another DD target while being dragged
33967          * @param {Grid} this
33968          * @param {Roo.GridDD} dd The drag drop object
33969          * @param {String} targetId The target drag drop object
33970          * @param {event} e The raw browser event
33971          */
33972         "dragout" : true,
33973         /**
33974          * @event rowclass
33975          * Fires when a row is rendered, so you can change add a style to it.
33976          * @param {GridView} gridview   The grid view
33977          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33978          */
33979         'rowclass' : true,
33980
33981         /**
33982          * @event render
33983          * Fires when the grid is rendered
33984          * @param {Grid} grid
33985          */
33986         'render' : true
33987     });
33988
33989     Roo.grid.Grid.superclass.constructor.call(this);
33990 };
33991 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33992     
33993     /**
33994          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
33995          */
33996         /**
33997          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
33998          */
33999         /**
34000          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
34001          */
34002         /**
34003          * @cfg {Roo.grid.Store} ds The data store for the grid
34004          */
34005         /**
34006          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
34007          */
34008         /**
34009      * @cfg {String} ddGroup - drag drop group.
34010      */
34011       /**
34012      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
34013      */
34014
34015     /**
34016      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34017      */
34018     minColumnWidth : 25,
34019
34020     /**
34021      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34022      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34023      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34024      */
34025     autoSizeColumns : false,
34026
34027     /**
34028      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34029      */
34030     autoSizeHeaders : true,
34031
34032     /**
34033      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34034      */
34035     monitorWindowResize : true,
34036
34037     /**
34038      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34039      * rows measured to get a columns size. Default is 0 (all rows).
34040      */
34041     maxRowsToMeasure : 0,
34042
34043     /**
34044      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34045      */
34046     trackMouseOver : true,
34047
34048     /**
34049     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34050     */
34051       /**
34052     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
34053     */
34054     
34055     /**
34056     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34057     */
34058     enableDragDrop : false,
34059     
34060     /**
34061     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34062     */
34063     enableColumnMove : true,
34064     
34065     /**
34066     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34067     */
34068     enableColumnHide : true,
34069     
34070     /**
34071     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34072     */
34073     enableRowHeightSync : false,
34074     
34075     /**
34076     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34077     */
34078     stripeRows : true,
34079     
34080     /**
34081     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34082     */
34083     autoHeight : false,
34084
34085     /**
34086      * @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.
34087      */
34088     autoExpandColumn : false,
34089
34090     /**
34091     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34092     * Default is 50.
34093     */
34094     autoExpandMin : 50,
34095
34096     /**
34097     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34098     */
34099     autoExpandMax : 1000,
34100
34101     /**
34102     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34103     */
34104     view : null,
34105
34106     /**
34107     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34108     */
34109     loadMask : false,
34110     /**
34111     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34112     */
34113     dropTarget: false,
34114     
34115    
34116     
34117     // private
34118     rendered : false,
34119
34120     /**
34121     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34122     * of a fixed width. Default is false.
34123     */
34124     /**
34125     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34126     */
34127     
34128     
34129     /**
34130     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34131     * %0 is replaced with the number of selected rows.
34132     */
34133     ddText : "{0} selected row{1}",
34134     
34135     
34136     /**
34137      * Called once after all setup has been completed and the grid is ready to be rendered.
34138      * @return {Roo.grid.Grid} this
34139      */
34140     render : function()
34141     {
34142         var c = this.container;
34143         // try to detect autoHeight/width mode
34144         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34145             this.autoHeight = true;
34146         }
34147         var view = this.getView();
34148         view.init(this);
34149
34150         c.on("click", this.onClick, this);
34151         c.on("dblclick", this.onDblClick, this);
34152         c.on("contextmenu", this.onContextMenu, this);
34153         c.on("keydown", this.onKeyDown, this);
34154         if (Roo.isTouch) {
34155             c.on("touchstart", this.onTouchStart, this);
34156         }
34157
34158         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34159
34160         this.getSelectionModel().init(this);
34161
34162         view.render();
34163
34164         if(this.loadMask){
34165             this.loadMask = new Roo.LoadMask(this.container,
34166                     Roo.apply({store:this.dataSource}, this.loadMask));
34167         }
34168         
34169         
34170         if (this.toolbar && this.toolbar.xtype) {
34171             this.toolbar.container = this.getView().getHeaderPanel(true);
34172             this.toolbar = new Roo.Toolbar(this.toolbar);
34173         }
34174         if (this.footer && this.footer.xtype) {
34175             this.footer.dataSource = this.getDataSource();
34176             this.footer.container = this.getView().getFooterPanel(true);
34177             this.footer = Roo.factory(this.footer, Roo);
34178         }
34179         if (this.dropTarget && this.dropTarget.xtype) {
34180             delete this.dropTarget.xtype;
34181             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34182         }
34183         
34184         
34185         this.rendered = true;
34186         this.fireEvent('render', this);
34187         return this;
34188     },
34189
34190     /**
34191      * Reconfigures the grid to use a different Store and Column Model.
34192      * The View will be bound to the new objects and refreshed.
34193      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34194      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34195      */
34196     reconfigure : function(dataSource, colModel){
34197         if(this.loadMask){
34198             this.loadMask.destroy();
34199             this.loadMask = new Roo.LoadMask(this.container,
34200                     Roo.apply({store:dataSource}, this.loadMask));
34201         }
34202         this.view.bind(dataSource, colModel);
34203         this.dataSource = dataSource;
34204         this.colModel = colModel;
34205         this.view.refresh(true);
34206     },
34207     /**
34208      * addColumns
34209      * Add's a column, default at the end..
34210      
34211      * @param {int} position to add (default end)
34212      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
34213      */
34214     addColumns : function(pos, ar)
34215     {
34216         
34217         for (var i =0;i< ar.length;i++) {
34218             var cfg = ar[i];
34219             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
34220             this.cm.lookup[cfg.id] = cfg;
34221         }
34222         
34223         
34224         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
34225             pos = this.cm.config.length; //this.cm.config.push(cfg);
34226         } 
34227         pos = Math.max(0,pos);
34228         ar.unshift(0);
34229         ar.unshift(pos);
34230         this.cm.config.splice.apply(this.cm.config, ar);
34231         
34232         
34233         
34234         this.view.generateRules(this.cm);
34235         this.view.refresh(true);
34236         
34237     },
34238     
34239     
34240     
34241     
34242     // private
34243     onKeyDown : function(e){
34244         this.fireEvent("keydown", e);
34245     },
34246
34247     /**
34248      * Destroy this grid.
34249      * @param {Boolean} removeEl True to remove the element
34250      */
34251     destroy : function(removeEl, keepListeners){
34252         if(this.loadMask){
34253             this.loadMask.destroy();
34254         }
34255         var c = this.container;
34256         c.removeAllListeners();
34257         this.view.destroy();
34258         this.colModel.purgeListeners();
34259         if(!keepListeners){
34260             this.purgeListeners();
34261         }
34262         c.update("");
34263         if(removeEl === true){
34264             c.remove();
34265         }
34266     },
34267
34268     // private
34269     processEvent : function(name, e){
34270         // does this fire select???
34271         //Roo.log('grid:processEvent '  + name);
34272         
34273         if (name != 'touchstart' ) {
34274             this.fireEvent(name, e);    
34275         }
34276         
34277         var t = e.getTarget();
34278         var v = this.view;
34279         var header = v.findHeaderIndex(t);
34280         if(header !== false){
34281             var ename = name == 'touchstart' ? 'click' : name;
34282              
34283             this.fireEvent("header" + ename, this, header, e);
34284         }else{
34285             var row = v.findRowIndex(t);
34286             var cell = v.findCellIndex(t);
34287             if (name == 'touchstart') {
34288                 // first touch is always a click.
34289                 // hopefull this happens after selection is updated.?
34290                 name = false;
34291                 
34292                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
34293                     var cs = this.selModel.getSelectedCell();
34294                     if (row == cs[0] && cell == cs[1]){
34295                         name = 'dblclick';
34296                     }
34297                 }
34298                 if (typeof(this.selModel.getSelections) != 'undefined') {
34299                     var cs = this.selModel.getSelections();
34300                     var ds = this.dataSource;
34301                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
34302                         name = 'dblclick';
34303                     }
34304                 }
34305                 if (!name) {
34306                     return;
34307                 }
34308             }
34309             
34310             
34311             if(row !== false){
34312                 this.fireEvent("row" + name, this, row, e);
34313                 if(cell !== false){
34314                     this.fireEvent("cell" + name, this, row, cell, e);
34315                 }
34316             }
34317         }
34318     },
34319
34320     // private
34321     onClick : function(e){
34322         this.processEvent("click", e);
34323     },
34324    // private
34325     onTouchStart : function(e){
34326         this.processEvent("touchstart", e);
34327     },
34328
34329     // private
34330     onContextMenu : function(e, t){
34331         this.processEvent("contextmenu", e);
34332     },
34333
34334     // private
34335     onDblClick : function(e){
34336         this.processEvent("dblclick", e);
34337     },
34338
34339     // private
34340     walkCells : function(row, col, step, fn, scope){
34341         var cm = this.colModel, clen = cm.getColumnCount();
34342         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34343         if(step < 0){
34344             if(col < 0){
34345                 row--;
34346                 first = false;
34347             }
34348             while(row >= 0){
34349                 if(!first){
34350                     col = clen-1;
34351                 }
34352                 first = false;
34353                 while(col >= 0){
34354                     if(fn.call(scope || this, row, col, cm) === true){
34355                         return [row, col];
34356                     }
34357                     col--;
34358                 }
34359                 row--;
34360             }
34361         } else {
34362             if(col >= clen){
34363                 row++;
34364                 first = false;
34365             }
34366             while(row < rlen){
34367                 if(!first){
34368                     col = 0;
34369                 }
34370                 first = false;
34371                 while(col < clen){
34372                     if(fn.call(scope || this, row, col, cm) === true){
34373                         return [row, col];
34374                     }
34375                     col++;
34376                 }
34377                 row++;
34378             }
34379         }
34380         return null;
34381     },
34382
34383     // private
34384     getSelections : function(){
34385         return this.selModel.getSelections();
34386     },
34387
34388     /**
34389      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34390      * but if manual update is required this method will initiate it.
34391      */
34392     autoSize : function(){
34393         if(this.rendered){
34394             this.view.layout();
34395             if(this.view.adjustForScroll){
34396                 this.view.adjustForScroll();
34397             }
34398         }
34399     },
34400
34401     /**
34402      * Returns the grid's underlying element.
34403      * @return {Element} The element
34404      */
34405     getGridEl : function(){
34406         return this.container;
34407     },
34408
34409     // private for compatibility, overridden by editor grid
34410     stopEditing : function(){},
34411
34412     /**
34413      * Returns the grid's SelectionModel.
34414      * @return {SelectionModel}
34415      */
34416     getSelectionModel : function(){
34417         if(!this.selModel){
34418             this.selModel = new Roo.grid.RowSelectionModel();
34419         }
34420         return this.selModel;
34421     },
34422
34423     /**
34424      * Returns the grid's DataSource.
34425      * @return {DataSource}
34426      */
34427     getDataSource : function(){
34428         return this.dataSource;
34429     },
34430
34431     /**
34432      * Returns the grid's ColumnModel.
34433      * @return {ColumnModel}
34434      */
34435     getColumnModel : function(){
34436         return this.colModel;
34437     },
34438
34439     /**
34440      * Returns the grid's GridView object.
34441      * @return {GridView}
34442      */
34443     getView : function(){
34444         if(!this.view){
34445             this.view = new Roo.grid.GridView(this.viewConfig);
34446             this.relayEvents(this.view, [
34447                 "beforerowremoved", "beforerowsinserted",
34448                 "beforerefresh", "rowremoved",
34449                 "rowsinserted", "rowupdated" ,"refresh"
34450             ]);
34451         }
34452         return this.view;
34453     },
34454     /**
34455      * Called to get grid's drag proxy text, by default returns this.ddText.
34456      * Override this to put something different in the dragged text.
34457      * @return {String}
34458      */
34459     getDragDropText : function(){
34460         var count = this.selModel.getCount();
34461         return String.format(this.ddText, count, count == 1 ? '' : 's');
34462     }
34463 });
34464 /*
34465  * Based on:
34466  * Ext JS Library 1.1.1
34467  * Copyright(c) 2006-2007, Ext JS, LLC.
34468  *
34469  * Originally Released Under LGPL - original licence link has changed is not relivant.
34470  *
34471  * Fork - LGPL
34472  * <script type="text/javascript">
34473  */
34474  /**
34475  * @class Roo.grid.AbstractGridView
34476  * @extends Roo.util.Observable
34477  * @abstract
34478  * Abstract base class for grid Views
34479  * @constructor
34480  */
34481 Roo.grid.AbstractGridView = function(){
34482         this.grid = null;
34483         
34484         this.events = {
34485             "beforerowremoved" : true,
34486             "beforerowsinserted" : true,
34487             "beforerefresh" : true,
34488             "rowremoved" : true,
34489             "rowsinserted" : true,
34490             "rowupdated" : true,
34491             "refresh" : true
34492         };
34493     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34494 };
34495
34496 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34497     rowClass : "x-grid-row",
34498     cellClass : "x-grid-cell",
34499     tdClass : "x-grid-td",
34500     hdClass : "x-grid-hd",
34501     splitClass : "x-grid-hd-split",
34502     
34503     init: function(grid){
34504         this.grid = grid;
34505                 var cid = this.grid.getGridEl().id;
34506         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34507         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34508         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34509         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34510         },
34511         
34512     getColumnRenderers : function(){
34513         var renderers = [];
34514         var cm = this.grid.colModel;
34515         var colCount = cm.getColumnCount();
34516         for(var i = 0; i < colCount; i++){
34517             renderers[i] = cm.getRenderer(i);
34518         }
34519         return renderers;
34520     },
34521     
34522     getColumnIds : function(){
34523         var ids = [];
34524         var cm = this.grid.colModel;
34525         var colCount = cm.getColumnCount();
34526         for(var i = 0; i < colCount; i++){
34527             ids[i] = cm.getColumnId(i);
34528         }
34529         return ids;
34530     },
34531     
34532     getDataIndexes : function(){
34533         if(!this.indexMap){
34534             this.indexMap = this.buildIndexMap();
34535         }
34536         return this.indexMap.colToData;
34537     },
34538     
34539     getColumnIndexByDataIndex : function(dataIndex){
34540         if(!this.indexMap){
34541             this.indexMap = this.buildIndexMap();
34542         }
34543         return this.indexMap.dataToCol[dataIndex];
34544     },
34545     
34546     /**
34547      * Set a css style for a column dynamically. 
34548      * @param {Number} colIndex The index of the column
34549      * @param {String} name The css property name
34550      * @param {String} value The css value
34551      */
34552     setCSSStyle : function(colIndex, name, value){
34553         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34554         Roo.util.CSS.updateRule(selector, name, value);
34555     },
34556     
34557     generateRules : function(cm){
34558         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34559         Roo.util.CSS.removeStyleSheet(rulesId);
34560         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34561             var cid = cm.getColumnId(i);
34562             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34563                          this.tdSelector, cid, " {\n}\n",
34564                          this.hdSelector, cid, " {\n}\n",
34565                          this.splitSelector, cid, " {\n}\n");
34566         }
34567         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34568     }
34569 });/*
34570  * Based on:
34571  * Ext JS Library 1.1.1
34572  * Copyright(c) 2006-2007, Ext JS, LLC.
34573  *
34574  * Originally Released Under LGPL - original licence link has changed is not relivant.
34575  *
34576  * Fork - LGPL
34577  * <script type="text/javascript">
34578  */
34579
34580 // private
34581 // This is a support class used internally by the Grid components
34582 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34583     this.grid = grid;
34584     this.view = grid.getView();
34585     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34586     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34587     if(hd2){
34588         this.setHandleElId(Roo.id(hd));
34589         this.setOuterHandleElId(Roo.id(hd2));
34590     }
34591     this.scroll = false;
34592 };
34593 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34594     maxDragWidth: 120,
34595     getDragData : function(e){
34596         var t = Roo.lib.Event.getTarget(e);
34597         var h = this.view.findHeaderCell(t);
34598         if(h){
34599             return {ddel: h.firstChild, header:h};
34600         }
34601         return false;
34602     },
34603
34604     onInitDrag : function(e){
34605         this.view.headersDisabled = true;
34606         var clone = this.dragData.ddel.cloneNode(true);
34607         clone.id = Roo.id();
34608         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34609         this.proxy.update(clone);
34610         return true;
34611     },
34612
34613     afterValidDrop : function(){
34614         var v = this.view;
34615         setTimeout(function(){
34616             v.headersDisabled = false;
34617         }, 50);
34618     },
34619
34620     afterInvalidDrop : function(){
34621         var v = this.view;
34622         setTimeout(function(){
34623             v.headersDisabled = false;
34624         }, 50);
34625     }
34626 });
34627 /*
34628  * Based on:
34629  * Ext JS Library 1.1.1
34630  * Copyright(c) 2006-2007, Ext JS, LLC.
34631  *
34632  * Originally Released Under LGPL - original licence link has changed is not relivant.
34633  *
34634  * Fork - LGPL
34635  * <script type="text/javascript">
34636  */
34637 // private
34638 // This is a support class used internally by the Grid components
34639 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34640     this.grid = grid;
34641     this.view = grid.getView();
34642     // split the proxies so they don't interfere with mouse events
34643     this.proxyTop = Roo.DomHelper.append(document.body, {
34644         cls:"col-move-top", html:"&#160;"
34645     }, true);
34646     this.proxyBottom = Roo.DomHelper.append(document.body, {
34647         cls:"col-move-bottom", html:"&#160;"
34648     }, true);
34649     this.proxyTop.hide = this.proxyBottom.hide = function(){
34650         this.setLeftTop(-100,-100);
34651         this.setStyle("visibility", "hidden");
34652     };
34653     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34654     // temporarily disabled
34655     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34656     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34657 };
34658 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34659     proxyOffsets : [-4, -9],
34660     fly: Roo.Element.fly,
34661
34662     getTargetFromEvent : function(e){
34663         var t = Roo.lib.Event.getTarget(e);
34664         var cindex = this.view.findCellIndex(t);
34665         if(cindex !== false){
34666             return this.view.getHeaderCell(cindex);
34667         }
34668         return null;
34669     },
34670
34671     nextVisible : function(h){
34672         var v = this.view, cm = this.grid.colModel;
34673         h = h.nextSibling;
34674         while(h){
34675             if(!cm.isHidden(v.getCellIndex(h))){
34676                 return h;
34677             }
34678             h = h.nextSibling;
34679         }
34680         return null;
34681     },
34682
34683     prevVisible : function(h){
34684         var v = this.view, cm = this.grid.colModel;
34685         h = h.prevSibling;
34686         while(h){
34687             if(!cm.isHidden(v.getCellIndex(h))){
34688                 return h;
34689             }
34690             h = h.prevSibling;
34691         }
34692         return null;
34693     },
34694
34695     positionIndicator : function(h, n, e){
34696         var x = Roo.lib.Event.getPageX(e);
34697         var r = Roo.lib.Dom.getRegion(n.firstChild);
34698         var px, pt, py = r.top + this.proxyOffsets[1];
34699         if((r.right - x) <= (r.right-r.left)/2){
34700             px = r.right+this.view.borderWidth;
34701             pt = "after";
34702         }else{
34703             px = r.left;
34704             pt = "before";
34705         }
34706         var oldIndex = this.view.getCellIndex(h);
34707         var newIndex = this.view.getCellIndex(n);
34708
34709         if(this.grid.colModel.isFixed(newIndex)){
34710             return false;
34711         }
34712
34713         var locked = this.grid.colModel.isLocked(newIndex);
34714
34715         if(pt == "after"){
34716             newIndex++;
34717         }
34718         if(oldIndex < newIndex){
34719             newIndex--;
34720         }
34721         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34722             return false;
34723         }
34724         px +=  this.proxyOffsets[0];
34725         this.proxyTop.setLeftTop(px, py);
34726         this.proxyTop.show();
34727         if(!this.bottomOffset){
34728             this.bottomOffset = this.view.mainHd.getHeight();
34729         }
34730         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34731         this.proxyBottom.show();
34732         return pt;
34733     },
34734
34735     onNodeEnter : function(n, dd, e, data){
34736         if(data.header != n){
34737             this.positionIndicator(data.header, n, e);
34738         }
34739     },
34740
34741     onNodeOver : function(n, dd, e, data){
34742         var result = false;
34743         if(data.header != n){
34744             result = this.positionIndicator(data.header, n, e);
34745         }
34746         if(!result){
34747             this.proxyTop.hide();
34748             this.proxyBottom.hide();
34749         }
34750         return result ? this.dropAllowed : this.dropNotAllowed;
34751     },
34752
34753     onNodeOut : function(n, dd, e, data){
34754         this.proxyTop.hide();
34755         this.proxyBottom.hide();
34756     },
34757
34758     onNodeDrop : function(n, dd, e, data){
34759         var h = data.header;
34760         if(h != n){
34761             var cm = this.grid.colModel;
34762             var x = Roo.lib.Event.getPageX(e);
34763             var r = Roo.lib.Dom.getRegion(n.firstChild);
34764             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34765             var oldIndex = this.view.getCellIndex(h);
34766             var newIndex = this.view.getCellIndex(n);
34767             var locked = cm.isLocked(newIndex);
34768             if(pt == "after"){
34769                 newIndex++;
34770             }
34771             if(oldIndex < newIndex){
34772                 newIndex--;
34773             }
34774             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34775                 return false;
34776             }
34777             cm.setLocked(oldIndex, locked, true);
34778             cm.moveColumn(oldIndex, newIndex);
34779             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34780             return true;
34781         }
34782         return false;
34783     }
34784 });
34785 /*
34786  * Based on:
34787  * Ext JS Library 1.1.1
34788  * Copyright(c) 2006-2007, Ext JS, LLC.
34789  *
34790  * Originally Released Under LGPL - original licence link has changed is not relivant.
34791  *
34792  * Fork - LGPL
34793  * <script type="text/javascript">
34794  */
34795   
34796 /**
34797  * @class Roo.grid.GridView
34798  * @extends Roo.util.Observable
34799  *
34800  * @constructor
34801  * @param {Object} config
34802  */
34803 Roo.grid.GridView = function(config){
34804     Roo.grid.GridView.superclass.constructor.call(this);
34805     this.el = null;
34806
34807     Roo.apply(this, config);
34808 };
34809
34810 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34811
34812     unselectable :  'unselectable="on"',
34813     unselectableCls :  'x-unselectable',
34814     
34815     
34816     rowClass : "x-grid-row",
34817
34818     cellClass : "x-grid-col",
34819
34820     tdClass : "x-grid-td",
34821
34822     hdClass : "x-grid-hd",
34823
34824     splitClass : "x-grid-split",
34825
34826     sortClasses : ["sort-asc", "sort-desc"],
34827
34828     enableMoveAnim : false,
34829
34830     hlColor: "C3DAF9",
34831
34832     dh : Roo.DomHelper,
34833
34834     fly : Roo.Element.fly,
34835
34836     css : Roo.util.CSS,
34837
34838     borderWidth: 1,
34839
34840     splitOffset: 3,
34841
34842     scrollIncrement : 22,
34843
34844     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34845
34846     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34847
34848     bind : function(ds, cm){
34849         if(this.ds){
34850             this.ds.un("load", this.onLoad, this);
34851             this.ds.un("datachanged", this.onDataChange, this);
34852             this.ds.un("add", this.onAdd, this);
34853             this.ds.un("remove", this.onRemove, this);
34854             this.ds.un("update", this.onUpdate, this);
34855             this.ds.un("clear", this.onClear, this);
34856         }
34857         if(ds){
34858             ds.on("load", this.onLoad, this);
34859             ds.on("datachanged", this.onDataChange, this);
34860             ds.on("add", this.onAdd, this);
34861             ds.on("remove", this.onRemove, this);
34862             ds.on("update", this.onUpdate, this);
34863             ds.on("clear", this.onClear, this);
34864         }
34865         this.ds = ds;
34866
34867         if(this.cm){
34868             this.cm.un("widthchange", this.onColWidthChange, this);
34869             this.cm.un("headerchange", this.onHeaderChange, this);
34870             this.cm.un("hiddenchange", this.onHiddenChange, this);
34871             this.cm.un("columnmoved", this.onColumnMove, this);
34872             this.cm.un("columnlockchange", this.onColumnLock, this);
34873         }
34874         if(cm){
34875             this.generateRules(cm);
34876             cm.on("widthchange", this.onColWidthChange, this);
34877             cm.on("headerchange", this.onHeaderChange, this);
34878             cm.on("hiddenchange", this.onHiddenChange, this);
34879             cm.on("columnmoved", this.onColumnMove, this);
34880             cm.on("columnlockchange", this.onColumnLock, this);
34881         }
34882         this.cm = cm;
34883     },
34884
34885     init: function(grid){
34886         Roo.grid.GridView.superclass.init.call(this, grid);
34887
34888         this.bind(grid.dataSource, grid.colModel);
34889
34890         grid.on("headerclick", this.handleHeaderClick, this);
34891
34892         if(grid.trackMouseOver){
34893             grid.on("mouseover", this.onRowOver, this);
34894             grid.on("mouseout", this.onRowOut, this);
34895         }
34896         grid.cancelTextSelection = function(){};
34897         this.gridId = grid.id;
34898
34899         var tpls = this.templates || {};
34900
34901         if(!tpls.master){
34902             tpls.master = new Roo.Template(
34903                '<div class="x-grid" hidefocus="true">',
34904                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34905                   '<div class="x-grid-topbar"></div>',
34906                   '<div class="x-grid-scroller"><div></div></div>',
34907                   '<div class="x-grid-locked">',
34908                       '<div class="x-grid-header">{lockedHeader}</div>',
34909                       '<div class="x-grid-body">{lockedBody}</div>',
34910                   "</div>",
34911                   '<div class="x-grid-viewport">',
34912                       '<div class="x-grid-header">{header}</div>',
34913                       '<div class="x-grid-body">{body}</div>',
34914                   "</div>",
34915                   '<div class="x-grid-bottombar"></div>',
34916                  
34917                   '<div class="x-grid-resize-proxy">&#160;</div>',
34918                "</div>"
34919             );
34920             tpls.master.disableformats = true;
34921         }
34922
34923         if(!tpls.header){
34924             tpls.header = new Roo.Template(
34925                '<table border="0" cellspacing="0" cellpadding="0">',
34926                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34927                "</table>{splits}"
34928             );
34929             tpls.header.disableformats = true;
34930         }
34931         tpls.header.compile();
34932
34933         if(!tpls.hcell){
34934             tpls.hcell = new Roo.Template(
34935                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34936                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34937                 "</div></td>"
34938              );
34939              tpls.hcell.disableFormats = true;
34940         }
34941         tpls.hcell.compile();
34942
34943         if(!tpls.hsplit){
34944             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
34945                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
34946             tpls.hsplit.disableFormats = true;
34947         }
34948         tpls.hsplit.compile();
34949
34950         if(!tpls.body){
34951             tpls.body = new Roo.Template(
34952                '<table border="0" cellspacing="0" cellpadding="0">',
34953                "<tbody>{rows}</tbody>",
34954                "</table>"
34955             );
34956             tpls.body.disableFormats = true;
34957         }
34958         tpls.body.compile();
34959
34960         if(!tpls.row){
34961             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34962             tpls.row.disableFormats = true;
34963         }
34964         tpls.row.compile();
34965
34966         if(!tpls.cell){
34967             tpls.cell = new Roo.Template(
34968                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34969                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
34970                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
34971                 "</td>"
34972             );
34973             tpls.cell.disableFormats = true;
34974         }
34975         tpls.cell.compile();
34976
34977         this.templates = tpls;
34978     },
34979
34980     // remap these for backwards compat
34981     onColWidthChange : function(){
34982         this.updateColumns.apply(this, arguments);
34983     },
34984     onHeaderChange : function(){
34985         this.updateHeaders.apply(this, arguments);
34986     }, 
34987     onHiddenChange : function(){
34988         this.handleHiddenChange.apply(this, arguments);
34989     },
34990     onColumnMove : function(){
34991         this.handleColumnMove.apply(this, arguments);
34992     },
34993     onColumnLock : function(){
34994         this.handleLockChange.apply(this, arguments);
34995     },
34996
34997     onDataChange : function(){
34998         this.refresh();
34999         this.updateHeaderSortState();
35000     },
35001
35002     onClear : function(){
35003         this.refresh();
35004     },
35005
35006     onUpdate : function(ds, record){
35007         this.refreshRow(record);
35008     },
35009
35010     refreshRow : function(record){
35011         var ds = this.ds, index;
35012         if(typeof record == 'number'){
35013             index = record;
35014             record = ds.getAt(index);
35015         }else{
35016             index = ds.indexOf(record);
35017         }
35018         this.insertRows(ds, index, index, true);
35019         this.onRemove(ds, record, index+1, true);
35020         this.syncRowHeights(index, index);
35021         this.layout();
35022         this.fireEvent("rowupdated", this, index, record);
35023     },
35024
35025     onAdd : function(ds, records, index){
35026         this.insertRows(ds, index, index + (records.length-1));
35027     },
35028
35029     onRemove : function(ds, record, index, isUpdate){
35030         if(isUpdate !== true){
35031             this.fireEvent("beforerowremoved", this, index, record);
35032         }
35033         var bt = this.getBodyTable(), lt = this.getLockedTable();
35034         if(bt.rows[index]){
35035             bt.firstChild.removeChild(bt.rows[index]);
35036         }
35037         if(lt.rows[index]){
35038             lt.firstChild.removeChild(lt.rows[index]);
35039         }
35040         if(isUpdate !== true){
35041             this.stripeRows(index);
35042             this.syncRowHeights(index, index);
35043             this.layout();
35044             this.fireEvent("rowremoved", this, index, record);
35045         }
35046     },
35047
35048     onLoad : function(){
35049         this.scrollToTop();
35050     },
35051
35052     /**
35053      * Scrolls the grid to the top
35054      */
35055     scrollToTop : function(){
35056         if(this.scroller){
35057             this.scroller.dom.scrollTop = 0;
35058             this.syncScroll();
35059         }
35060     },
35061
35062     /**
35063      * Gets a panel in the header of the grid that can be used for toolbars etc.
35064      * After modifying the contents of this panel a call to grid.autoSize() may be
35065      * required to register any changes in size.
35066      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35067      * @return Roo.Element
35068      */
35069     getHeaderPanel : function(doShow){
35070         if(doShow){
35071             this.headerPanel.show();
35072         }
35073         return this.headerPanel;
35074     },
35075
35076     /**
35077      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35078      * After modifying the contents of this panel a call to grid.autoSize() may be
35079      * required to register any changes in size.
35080      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35081      * @return Roo.Element
35082      */
35083     getFooterPanel : function(doShow){
35084         if(doShow){
35085             this.footerPanel.show();
35086         }
35087         return this.footerPanel;
35088     },
35089
35090     initElements : function(){
35091         var E = Roo.Element;
35092         var el = this.grid.getGridEl().dom.firstChild;
35093         var cs = el.childNodes;
35094
35095         this.el = new E(el);
35096         
35097          this.focusEl = new E(el.firstChild);
35098         this.focusEl.swallowEvent("click", true);
35099         
35100         this.headerPanel = new E(cs[1]);
35101         this.headerPanel.enableDisplayMode("block");
35102
35103         this.scroller = new E(cs[2]);
35104         this.scrollSizer = new E(this.scroller.dom.firstChild);
35105
35106         this.lockedWrap = new E(cs[3]);
35107         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35108         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35109
35110         this.mainWrap = new E(cs[4]);
35111         this.mainHd = new E(this.mainWrap.dom.firstChild);
35112         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35113
35114         this.footerPanel = new E(cs[5]);
35115         this.footerPanel.enableDisplayMode("block");
35116
35117         this.resizeProxy = new E(cs[6]);
35118
35119         this.headerSelector = String.format(
35120            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35121            this.lockedHd.id, this.mainHd.id
35122         );
35123
35124         this.splitterSelector = String.format(
35125            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35126            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35127         );
35128     },
35129     idToCssName : function(s)
35130     {
35131         return s.replace(/[^a-z0-9]+/ig, '-');
35132     },
35133
35134     getHeaderCell : function(index){
35135         return Roo.DomQuery.select(this.headerSelector)[index];
35136     },
35137
35138     getHeaderCellMeasure : function(index){
35139         return this.getHeaderCell(index).firstChild;
35140     },
35141
35142     getHeaderCellText : function(index){
35143         return this.getHeaderCell(index).firstChild.firstChild;
35144     },
35145
35146     getLockedTable : function(){
35147         return this.lockedBody.dom.firstChild;
35148     },
35149
35150     getBodyTable : function(){
35151         return this.mainBody.dom.firstChild;
35152     },
35153
35154     getLockedRow : function(index){
35155         return this.getLockedTable().rows[index];
35156     },
35157
35158     getRow : function(index){
35159         return this.getBodyTable().rows[index];
35160     },
35161
35162     getRowComposite : function(index){
35163         if(!this.rowEl){
35164             this.rowEl = new Roo.CompositeElementLite();
35165         }
35166         var els = [], lrow, mrow;
35167         if(lrow = this.getLockedRow(index)){
35168             els.push(lrow);
35169         }
35170         if(mrow = this.getRow(index)){
35171             els.push(mrow);
35172         }
35173         this.rowEl.elements = els;
35174         return this.rowEl;
35175     },
35176     /**
35177      * Gets the 'td' of the cell
35178      * 
35179      * @param {Integer} rowIndex row to select
35180      * @param {Integer} colIndex column to select
35181      * 
35182      * @return {Object} 
35183      */
35184     getCell : function(rowIndex, colIndex){
35185         var locked = this.cm.getLockedCount();
35186         var source;
35187         if(colIndex < locked){
35188             source = this.lockedBody.dom.firstChild;
35189         }else{
35190             source = this.mainBody.dom.firstChild;
35191             colIndex -= locked;
35192         }
35193         return source.rows[rowIndex].childNodes[colIndex];
35194     },
35195
35196     getCellText : function(rowIndex, colIndex){
35197         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35198     },
35199
35200     getCellBox : function(cell){
35201         var b = this.fly(cell).getBox();
35202         if(Roo.isOpera){ // opera fails to report the Y
35203             b.y = cell.offsetTop + this.mainBody.getY();
35204         }
35205         return b;
35206     },
35207
35208     getCellIndex : function(cell){
35209         var id = String(cell.className).match(this.cellRE);
35210         if(id){
35211             return parseInt(id[1], 10);
35212         }
35213         return 0;
35214     },
35215
35216     findHeaderIndex : function(n){
35217         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35218         return r ? this.getCellIndex(r) : false;
35219     },
35220
35221     findHeaderCell : function(n){
35222         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35223         return r ? r : false;
35224     },
35225
35226     findRowIndex : function(n){
35227         if(!n){
35228             return false;
35229         }
35230         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35231         return r ? r.rowIndex : false;
35232     },
35233
35234     findCellIndex : function(node){
35235         var stop = this.el.dom;
35236         while(node && node != stop){
35237             if(this.findRE.test(node.className)){
35238                 return this.getCellIndex(node);
35239             }
35240             node = node.parentNode;
35241         }
35242         return false;
35243     },
35244
35245     getColumnId : function(index){
35246         return this.cm.getColumnId(index);
35247     },
35248
35249     getSplitters : function()
35250     {
35251         if(this.splitterSelector){
35252            return Roo.DomQuery.select(this.splitterSelector);
35253         }else{
35254             return null;
35255       }
35256     },
35257
35258     getSplitter : function(index){
35259         return this.getSplitters()[index];
35260     },
35261
35262     onRowOver : function(e, t){
35263         var row;
35264         if((row = this.findRowIndex(t)) !== false){
35265             this.getRowComposite(row).addClass("x-grid-row-over");
35266         }
35267     },
35268
35269     onRowOut : function(e, t){
35270         var row;
35271         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35272             this.getRowComposite(row).removeClass("x-grid-row-over");
35273         }
35274     },
35275
35276     renderHeaders : function(){
35277         var cm = this.cm;
35278         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35279         var cb = [], lb = [], sb = [], lsb = [], p = {};
35280         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35281             p.cellId = "x-grid-hd-0-" + i;
35282             p.splitId = "x-grid-csplit-0-" + i;
35283             p.id = cm.getColumnId(i);
35284             p.value = cm.getColumnHeader(i) || "";
35285             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
35286             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35287             if(!cm.isLocked(i)){
35288                 cb[cb.length] = ct.apply(p);
35289                 sb[sb.length] = st.apply(p);
35290             }else{
35291                 lb[lb.length] = ct.apply(p);
35292                 lsb[lsb.length] = st.apply(p);
35293             }
35294         }
35295         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35296                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35297     },
35298
35299     updateHeaders : function(){
35300         var html = this.renderHeaders();
35301         this.lockedHd.update(html[0]);
35302         this.mainHd.update(html[1]);
35303     },
35304
35305     /**
35306      * Focuses the specified row.
35307      * @param {Number} row The row index
35308      */
35309     focusRow : function(row)
35310     {
35311         //Roo.log('GridView.focusRow');
35312         var x = this.scroller.dom.scrollLeft;
35313         this.focusCell(row, 0, false);
35314         this.scroller.dom.scrollLeft = x;
35315     },
35316
35317     /**
35318      * Focuses the specified cell.
35319      * @param {Number} row The row index
35320      * @param {Number} col The column index
35321      * @param {Boolean} hscroll false to disable horizontal scrolling
35322      */
35323     focusCell : function(row, col, hscroll)
35324     {
35325         //Roo.log('GridView.focusCell');
35326         var el = this.ensureVisible(row, col, hscroll);
35327         this.focusEl.alignTo(el, "tl-tl");
35328         if(Roo.isGecko){
35329             this.focusEl.focus();
35330         }else{
35331             this.focusEl.focus.defer(1, this.focusEl);
35332         }
35333     },
35334
35335     /**
35336      * Scrolls the specified cell into view
35337      * @param {Number} row The row index
35338      * @param {Number} col The column index
35339      * @param {Boolean} hscroll false to disable horizontal scrolling
35340      */
35341     ensureVisible : function(row, col, hscroll)
35342     {
35343         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35344         //return null; //disable for testing.
35345         if(typeof row != "number"){
35346             row = row.rowIndex;
35347         }
35348         if(row < 0 && row >= this.ds.getCount()){
35349             return  null;
35350         }
35351         col = (col !== undefined ? col : 0);
35352         var cm = this.grid.colModel;
35353         while(cm.isHidden(col)){
35354             col++;
35355         }
35356
35357         var el = this.getCell(row, col);
35358         if(!el){
35359             return null;
35360         }
35361         var c = this.scroller.dom;
35362
35363         var ctop = parseInt(el.offsetTop, 10);
35364         var cleft = parseInt(el.offsetLeft, 10);
35365         var cbot = ctop + el.offsetHeight;
35366         var cright = cleft + el.offsetWidth;
35367         
35368         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35369         var stop = parseInt(c.scrollTop, 10);
35370         var sleft = parseInt(c.scrollLeft, 10);
35371         var sbot = stop + ch;
35372         var sright = sleft + c.clientWidth;
35373         /*
35374         Roo.log('GridView.ensureVisible:' +
35375                 ' ctop:' + ctop +
35376                 ' c.clientHeight:' + c.clientHeight +
35377                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35378                 ' stop:' + stop +
35379                 ' cbot:' + cbot +
35380                 ' sbot:' + sbot +
35381                 ' ch:' + ch  
35382                 );
35383         */
35384         if(ctop < stop){
35385             c.scrollTop = ctop;
35386             //Roo.log("set scrolltop to ctop DISABLE?");
35387         }else if(cbot > sbot){
35388             //Roo.log("set scrolltop to cbot-ch");
35389             c.scrollTop = cbot-ch;
35390         }
35391         
35392         if(hscroll !== false){
35393             if(cleft < sleft){
35394                 c.scrollLeft = cleft;
35395             }else if(cright > sright){
35396                 c.scrollLeft = cright-c.clientWidth;
35397             }
35398         }
35399          
35400         return el;
35401     },
35402
35403     updateColumns : function(){
35404         this.grid.stopEditing();
35405         var cm = this.grid.colModel, colIds = this.getColumnIds();
35406         //var totalWidth = cm.getTotalWidth();
35407         var pos = 0;
35408         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35409             //if(cm.isHidden(i)) continue;
35410             var w = cm.getColumnWidth(i);
35411             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35412             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35413         }
35414         this.updateSplitters();
35415     },
35416
35417     generateRules : function(cm){
35418         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35419         Roo.util.CSS.removeStyleSheet(rulesId);
35420         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35421             var cid = cm.getColumnId(i);
35422             var align = '';
35423             if(cm.config[i].align){
35424                 align = 'text-align:'+cm.config[i].align+';';
35425             }
35426             var hidden = '';
35427             if(cm.isHidden(i)){
35428                 hidden = 'display:none;';
35429             }
35430             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35431             ruleBuf.push(
35432                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35433                     this.hdSelector, cid, " {\n", align, width, "}\n",
35434                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35435                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35436         }
35437         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35438     },
35439
35440     updateSplitters : function(){
35441         var cm = this.cm, s = this.getSplitters();
35442         if(s){ // splitters not created yet
35443             var pos = 0, locked = true;
35444             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35445                 if(cm.isHidden(i)) {
35446                     continue;
35447                 }
35448                 var w = cm.getColumnWidth(i); // make sure it's a number
35449                 if(!cm.isLocked(i) && locked){
35450                     pos = 0;
35451                     locked = false;
35452                 }
35453                 pos += w;
35454                 s[i].style.left = (pos-this.splitOffset) + "px";
35455             }
35456         }
35457     },
35458
35459     handleHiddenChange : function(colModel, colIndex, hidden){
35460         if(hidden){
35461             this.hideColumn(colIndex);
35462         }else{
35463             this.unhideColumn(colIndex);
35464         }
35465     },
35466
35467     hideColumn : function(colIndex){
35468         var cid = this.getColumnId(colIndex);
35469         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35470         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35471         if(Roo.isSafari){
35472             this.updateHeaders();
35473         }
35474         this.updateSplitters();
35475         this.layout();
35476     },
35477
35478     unhideColumn : function(colIndex){
35479         var cid = this.getColumnId(colIndex);
35480         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35481         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35482
35483         if(Roo.isSafari){
35484             this.updateHeaders();
35485         }
35486         this.updateSplitters();
35487         this.layout();
35488     },
35489
35490     insertRows : function(dm, firstRow, lastRow, isUpdate){
35491         if(firstRow == 0 && lastRow == dm.getCount()-1){
35492             this.refresh();
35493         }else{
35494             if(!isUpdate){
35495                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35496             }
35497             var s = this.getScrollState();
35498             var markup = this.renderRows(firstRow, lastRow);
35499             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35500             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35501             this.restoreScroll(s);
35502             if(!isUpdate){
35503                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35504                 this.syncRowHeights(firstRow, lastRow);
35505                 this.stripeRows(firstRow);
35506                 this.layout();
35507             }
35508         }
35509     },
35510
35511     bufferRows : function(markup, target, index){
35512         var before = null, trows = target.rows, tbody = target.tBodies[0];
35513         if(index < trows.length){
35514             before = trows[index];
35515         }
35516         var b = document.createElement("div");
35517         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35518         var rows = b.firstChild.rows;
35519         for(var i = 0, len = rows.length; i < len; i++){
35520             if(before){
35521                 tbody.insertBefore(rows[0], before);
35522             }else{
35523                 tbody.appendChild(rows[0]);
35524             }
35525         }
35526         b.innerHTML = "";
35527         b = null;
35528     },
35529
35530     deleteRows : function(dm, firstRow, lastRow){
35531         if(dm.getRowCount()<1){
35532             this.fireEvent("beforerefresh", this);
35533             this.mainBody.update("");
35534             this.lockedBody.update("");
35535             this.fireEvent("refresh", this);
35536         }else{
35537             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35538             var bt = this.getBodyTable();
35539             var tbody = bt.firstChild;
35540             var rows = bt.rows;
35541             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35542                 tbody.removeChild(rows[firstRow]);
35543             }
35544             this.stripeRows(firstRow);
35545             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35546         }
35547     },
35548
35549     updateRows : function(dataSource, firstRow, lastRow){
35550         var s = this.getScrollState();
35551         this.refresh();
35552         this.restoreScroll(s);
35553     },
35554
35555     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35556         if(!noRefresh){
35557            this.refresh();
35558         }
35559         this.updateHeaderSortState();
35560     },
35561
35562     getScrollState : function(){
35563         
35564         var sb = this.scroller.dom;
35565         return {left: sb.scrollLeft, top: sb.scrollTop};
35566     },
35567
35568     stripeRows : function(startRow){
35569         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35570             return;
35571         }
35572         startRow = startRow || 0;
35573         var rows = this.getBodyTable().rows;
35574         var lrows = this.getLockedTable().rows;
35575         var cls = ' x-grid-row-alt ';
35576         for(var i = startRow, len = rows.length; i < len; i++){
35577             var row = rows[i], lrow = lrows[i];
35578             var isAlt = ((i+1) % 2 == 0);
35579             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35580             if(isAlt == hasAlt){
35581                 continue;
35582             }
35583             if(isAlt){
35584                 row.className += " x-grid-row-alt";
35585             }else{
35586                 row.className = row.className.replace("x-grid-row-alt", "");
35587             }
35588             if(lrow){
35589                 lrow.className = row.className;
35590             }
35591         }
35592     },
35593
35594     restoreScroll : function(state){
35595         //Roo.log('GridView.restoreScroll');
35596         var sb = this.scroller.dom;
35597         sb.scrollLeft = state.left;
35598         sb.scrollTop = state.top;
35599         this.syncScroll();
35600     },
35601
35602     syncScroll : function(){
35603         //Roo.log('GridView.syncScroll');
35604         var sb = this.scroller.dom;
35605         var sh = this.mainHd.dom;
35606         var bs = this.mainBody.dom;
35607         var lv = this.lockedBody.dom;
35608         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35609         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35610     },
35611
35612     handleScroll : function(e){
35613         this.syncScroll();
35614         var sb = this.scroller.dom;
35615         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35616         e.stopEvent();
35617     },
35618
35619     handleWheel : function(e){
35620         var d = e.getWheelDelta();
35621         this.scroller.dom.scrollTop -= d*22;
35622         // set this here to prevent jumpy scrolling on large tables
35623         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35624         e.stopEvent();
35625     },
35626
35627     renderRows : function(startRow, endRow){
35628         // pull in all the crap needed to render rows
35629         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35630         var colCount = cm.getColumnCount();
35631
35632         if(ds.getCount() < 1){
35633             return ["", ""];
35634         }
35635
35636         // build a map for all the columns
35637         var cs = [];
35638         for(var i = 0; i < colCount; i++){
35639             var name = cm.getDataIndex(i);
35640             cs[i] = {
35641                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35642                 renderer : cm.getRenderer(i),
35643                 id : cm.getColumnId(i),
35644                 locked : cm.isLocked(i),
35645                 has_editor : cm.isCellEditable(i)
35646             };
35647         }
35648
35649         startRow = startRow || 0;
35650         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35651
35652         // records to render
35653         var rs = ds.getRange(startRow, endRow);
35654
35655         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35656     },
35657
35658     // As much as I hate to duplicate code, this was branched because FireFox really hates
35659     // [].join("") on strings. The performance difference was substantial enough to
35660     // branch this function
35661     doRender : Roo.isGecko ?
35662             function(cs, rs, ds, startRow, colCount, stripe){
35663                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35664                 // buffers
35665                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35666                 
35667                 var hasListener = this.grid.hasListener('rowclass');
35668                 var rowcfg = {};
35669                 for(var j = 0, len = rs.length; j < len; j++){
35670                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35671                     for(var i = 0; i < colCount; i++){
35672                         c = cs[i];
35673                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35674                         p.id = c.id;
35675                         p.css = p.attr = "";
35676                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35677                         if(p.value == undefined || p.value === "") {
35678                             p.value = "&#160;";
35679                         }
35680                         if(c.has_editor){
35681                             p.css += ' x-grid-editable-cell';
35682                         }
35683                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
35684                             p.css +=  ' x-grid-dirty-cell';
35685                         }
35686                         var markup = ct.apply(p);
35687                         if(!c.locked){
35688                             cb+= markup;
35689                         }else{
35690                             lcb+= markup;
35691                         }
35692                     }
35693                     var alt = [];
35694                     if(stripe && ((rowIndex+1) % 2 == 0)){
35695                         alt.push("x-grid-row-alt")
35696                     }
35697                     if(r.dirty){
35698                         alt.push(  " x-grid-dirty-row");
35699                     }
35700                     rp.cells = lcb;
35701                     if(this.getRowClass){
35702                         alt.push(this.getRowClass(r, rowIndex));
35703                     }
35704                     if (hasListener) {
35705                         rowcfg = {
35706                              
35707                             record: r,
35708                             rowIndex : rowIndex,
35709                             rowClass : ''
35710                         };
35711                         this.grid.fireEvent('rowclass', this, rowcfg);
35712                         alt.push(rowcfg.rowClass);
35713                     }
35714                     rp.alt = alt.join(" ");
35715                     lbuf+= rt.apply(rp);
35716                     rp.cells = cb;
35717                     buf+=  rt.apply(rp);
35718                 }
35719                 return [lbuf, buf];
35720             } :
35721             function(cs, rs, ds, startRow, colCount, stripe){
35722                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35723                 // buffers
35724                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35725                 var hasListener = this.grid.hasListener('rowclass');
35726  
35727                 var rowcfg = {};
35728                 for(var j = 0, len = rs.length; j < len; j++){
35729                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35730                     for(var i = 0; i < colCount; i++){
35731                         c = cs[i];
35732                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35733                         p.id = c.id;
35734                         p.css = p.attr = "";
35735                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35736                         if(p.value == undefined || p.value === "") {
35737                             p.value = "&#160;";
35738                         }
35739                         //Roo.log(c);
35740                          if(c.has_editor){
35741                             p.css += ' x-grid-editable-cell';
35742                         }
35743                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35744                             p.css += ' x-grid-dirty-cell' 
35745                         }
35746                         
35747                         var markup = ct.apply(p);
35748                         if(!c.locked){
35749                             cb[cb.length] = markup;
35750                         }else{
35751                             lcb[lcb.length] = markup;
35752                         }
35753                     }
35754                     var alt = [];
35755                     if(stripe && ((rowIndex+1) % 2 == 0)){
35756                         alt.push( "x-grid-row-alt");
35757                     }
35758                     if(r.dirty){
35759                         alt.push(" x-grid-dirty-row");
35760                     }
35761                     rp.cells = lcb;
35762                     if(this.getRowClass){
35763                         alt.push( this.getRowClass(r, rowIndex));
35764                     }
35765                     if (hasListener) {
35766                         rowcfg = {
35767                              
35768                             record: r,
35769                             rowIndex : rowIndex,
35770                             rowClass : ''
35771                         };
35772                         this.grid.fireEvent('rowclass', this, rowcfg);
35773                         alt.push(rowcfg.rowClass);
35774                     }
35775                     
35776                     rp.alt = alt.join(" ");
35777                     rp.cells = lcb.join("");
35778                     lbuf[lbuf.length] = rt.apply(rp);
35779                     rp.cells = cb.join("");
35780                     buf[buf.length] =  rt.apply(rp);
35781                 }
35782                 return [lbuf.join(""), buf.join("")];
35783             },
35784
35785     renderBody : function(){
35786         var markup = this.renderRows();
35787         var bt = this.templates.body;
35788         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35789     },
35790
35791     /**
35792      * Refreshes the grid
35793      * @param {Boolean} headersToo
35794      */
35795     refresh : function(headersToo){
35796         this.fireEvent("beforerefresh", this);
35797         this.grid.stopEditing();
35798         var result = this.renderBody();
35799         this.lockedBody.update(result[0]);
35800         this.mainBody.update(result[1]);
35801         if(headersToo === true){
35802             this.updateHeaders();
35803             this.updateColumns();
35804             this.updateSplitters();
35805             this.updateHeaderSortState();
35806         }
35807         this.syncRowHeights();
35808         this.layout();
35809         this.fireEvent("refresh", this);
35810     },
35811
35812     handleColumnMove : function(cm, oldIndex, newIndex){
35813         this.indexMap = null;
35814         var s = this.getScrollState();
35815         this.refresh(true);
35816         this.restoreScroll(s);
35817         this.afterMove(newIndex);
35818     },
35819
35820     afterMove : function(colIndex){
35821         if(this.enableMoveAnim && Roo.enableFx){
35822             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35823         }
35824         // if multisort - fix sortOrder, and reload..
35825         if (this.grid.dataSource.multiSort) {
35826             // the we can call sort again..
35827             var dm = this.grid.dataSource;
35828             var cm = this.grid.colModel;
35829             var so = [];
35830             for(var i = 0; i < cm.config.length; i++ ) {
35831                 
35832                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35833                     continue; // dont' bother, it's not in sort list or being set.
35834                 }
35835                 
35836                 so.push(cm.config[i].dataIndex);
35837             };
35838             dm.sortOrder = so;
35839             dm.load(dm.lastOptions);
35840             
35841             
35842         }
35843         
35844     },
35845
35846     updateCell : function(dm, rowIndex, dataIndex){
35847         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35848         if(typeof colIndex == "undefined"){ // not present in grid
35849             return;
35850         }
35851         var cm = this.grid.colModel;
35852         var cell = this.getCell(rowIndex, colIndex);
35853         var cellText = this.getCellText(rowIndex, colIndex);
35854
35855         var p = {
35856             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35857             id : cm.getColumnId(colIndex),
35858             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35859         };
35860         var renderer = cm.getRenderer(colIndex);
35861         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35862         if(typeof val == "undefined" || val === "") {
35863             val = "&#160;";
35864         }
35865         cellText.innerHTML = val;
35866         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35867         this.syncRowHeights(rowIndex, rowIndex);
35868     },
35869
35870     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35871         var maxWidth = 0;
35872         if(this.grid.autoSizeHeaders){
35873             var h = this.getHeaderCellMeasure(colIndex);
35874             maxWidth = Math.max(maxWidth, h.scrollWidth);
35875         }
35876         var tb, index;
35877         if(this.cm.isLocked(colIndex)){
35878             tb = this.getLockedTable();
35879             index = colIndex;
35880         }else{
35881             tb = this.getBodyTable();
35882             index = colIndex - this.cm.getLockedCount();
35883         }
35884         if(tb && tb.rows){
35885             var rows = tb.rows;
35886             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35887             for(var i = 0; i < stopIndex; i++){
35888                 var cell = rows[i].childNodes[index].firstChild;
35889                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35890             }
35891         }
35892         return maxWidth + /*margin for error in IE*/ 5;
35893     },
35894     /**
35895      * Autofit a column to its content.
35896      * @param {Number} colIndex
35897      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35898      */
35899      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35900          if(this.cm.isHidden(colIndex)){
35901              return; // can't calc a hidden column
35902          }
35903         if(forceMinSize){
35904             var cid = this.cm.getColumnId(colIndex);
35905             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35906            if(this.grid.autoSizeHeaders){
35907                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35908            }
35909         }
35910         var newWidth = this.calcColumnWidth(colIndex);
35911         this.cm.setColumnWidth(colIndex,
35912             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35913         if(!suppressEvent){
35914             this.grid.fireEvent("columnresize", colIndex, newWidth);
35915         }
35916     },
35917
35918     /**
35919      * Autofits all columns to their content and then expands to fit any extra space in the grid
35920      */
35921      autoSizeColumns : function(){
35922         var cm = this.grid.colModel;
35923         var colCount = cm.getColumnCount();
35924         for(var i = 0; i < colCount; i++){
35925             this.autoSizeColumn(i, true, true);
35926         }
35927         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35928             this.fitColumns();
35929         }else{
35930             this.updateColumns();
35931             this.layout();
35932         }
35933     },
35934
35935     /**
35936      * Autofits all columns to the grid's width proportionate with their current size
35937      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35938      */
35939     fitColumns : function(reserveScrollSpace){
35940         var cm = this.grid.colModel;
35941         var colCount = cm.getColumnCount();
35942         var cols = [];
35943         var width = 0;
35944         var i, w;
35945         for (i = 0; i < colCount; i++){
35946             if(!cm.isHidden(i) && !cm.isFixed(i)){
35947                 w = cm.getColumnWidth(i);
35948                 cols.push(i);
35949                 cols.push(w);
35950                 width += w;
35951             }
35952         }
35953         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35954         if(reserveScrollSpace){
35955             avail -= 17;
35956         }
35957         var frac = (avail - cm.getTotalWidth())/width;
35958         while (cols.length){
35959             w = cols.pop();
35960             i = cols.pop();
35961             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35962         }
35963         this.updateColumns();
35964         this.layout();
35965     },
35966
35967     onRowSelect : function(rowIndex){
35968         var row = this.getRowComposite(rowIndex);
35969         row.addClass("x-grid-row-selected");
35970     },
35971
35972     onRowDeselect : function(rowIndex){
35973         var row = this.getRowComposite(rowIndex);
35974         row.removeClass("x-grid-row-selected");
35975     },
35976
35977     onCellSelect : function(row, col){
35978         var cell = this.getCell(row, col);
35979         if(cell){
35980             Roo.fly(cell).addClass("x-grid-cell-selected");
35981         }
35982     },
35983
35984     onCellDeselect : function(row, col){
35985         var cell = this.getCell(row, col);
35986         if(cell){
35987             Roo.fly(cell).removeClass("x-grid-cell-selected");
35988         }
35989     },
35990
35991     updateHeaderSortState : function(){
35992         
35993         // sort state can be single { field: xxx, direction : yyy}
35994         // or   { xxx=>ASC , yyy : DESC ..... }
35995         
35996         var mstate = {};
35997         if (!this.ds.multiSort) { 
35998             var state = this.ds.getSortState();
35999             if(!state){
36000                 return;
36001             }
36002             mstate[state.field] = state.direction;
36003             // FIXME... - this is not used here.. but might be elsewhere..
36004             this.sortState = state;
36005             
36006         } else {
36007             mstate = this.ds.sortToggle;
36008         }
36009         //remove existing sort classes..
36010         
36011         var sc = this.sortClasses;
36012         var hds = this.el.select(this.headerSelector).removeClass(sc);
36013         
36014         for(var f in mstate) {
36015         
36016             var sortColumn = this.cm.findColumnIndex(f);
36017             
36018             if(sortColumn != -1){
36019                 var sortDir = mstate[f];        
36020                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36021             }
36022         }
36023         
36024          
36025         
36026     },
36027
36028
36029     handleHeaderClick : function(g, index,e){
36030         
36031         Roo.log("header click");
36032         
36033         if (Roo.isTouch) {
36034             // touch events on header are handled by context
36035             this.handleHdCtx(g,index,e);
36036             return;
36037         }
36038         
36039         
36040         if(this.headersDisabled){
36041             return;
36042         }
36043         var dm = g.dataSource, cm = g.colModel;
36044         if(!cm.isSortable(index)){
36045             return;
36046         }
36047         g.stopEditing();
36048         
36049         if (dm.multiSort) {
36050             // update the sortOrder
36051             var so = [];
36052             for(var i = 0; i < cm.config.length; i++ ) {
36053                 
36054                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36055                     continue; // dont' bother, it's not in sort list or being set.
36056                 }
36057                 
36058                 so.push(cm.config[i].dataIndex);
36059             };
36060             dm.sortOrder = so;
36061         }
36062         
36063         
36064         dm.sort(cm.getDataIndex(index));
36065     },
36066
36067
36068     destroy : function(){
36069         if(this.colMenu){
36070             this.colMenu.removeAll();
36071             Roo.menu.MenuMgr.unregister(this.colMenu);
36072             this.colMenu.getEl().remove();
36073             delete this.colMenu;
36074         }
36075         if(this.hmenu){
36076             this.hmenu.removeAll();
36077             Roo.menu.MenuMgr.unregister(this.hmenu);
36078             this.hmenu.getEl().remove();
36079             delete this.hmenu;
36080         }
36081         if(this.grid.enableColumnMove){
36082             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36083             if(dds){
36084                 for(var dd in dds){
36085                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36086                         var elid = dds[dd].dragElId;
36087                         dds[dd].unreg();
36088                         Roo.get(elid).remove();
36089                     } else if(dds[dd].config.isTarget){
36090                         dds[dd].proxyTop.remove();
36091                         dds[dd].proxyBottom.remove();
36092                         dds[dd].unreg();
36093                     }
36094                     if(Roo.dd.DDM.locationCache[dd]){
36095                         delete Roo.dd.DDM.locationCache[dd];
36096                     }
36097                 }
36098                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36099             }
36100         }
36101         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36102         this.bind(null, null);
36103         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36104     },
36105
36106     handleLockChange : function(){
36107         this.refresh(true);
36108     },
36109
36110     onDenyColumnLock : function(){
36111
36112     },
36113
36114     onDenyColumnHide : function(){
36115
36116     },
36117
36118     handleHdMenuClick : function(item){
36119         var index = this.hdCtxIndex;
36120         var cm = this.cm, ds = this.ds;
36121         switch(item.id){
36122             case "asc":
36123                 ds.sort(cm.getDataIndex(index), "ASC");
36124                 break;
36125             case "desc":
36126                 ds.sort(cm.getDataIndex(index), "DESC");
36127                 break;
36128             case "lock":
36129                 var lc = cm.getLockedCount();
36130                 if(cm.getColumnCount(true) <= lc+1){
36131                     this.onDenyColumnLock();
36132                     return;
36133                 }
36134                 if(lc != index){
36135                     cm.setLocked(index, true, true);
36136                     cm.moveColumn(index, lc);
36137                     this.grid.fireEvent("columnmove", index, lc);
36138                 }else{
36139                     cm.setLocked(index, true);
36140                 }
36141             break;
36142             case "unlock":
36143                 var lc = cm.getLockedCount();
36144                 if((lc-1) != index){
36145                     cm.setLocked(index, false, true);
36146                     cm.moveColumn(index, lc-1);
36147                     this.grid.fireEvent("columnmove", index, lc-1);
36148                 }else{
36149                     cm.setLocked(index, false);
36150                 }
36151             break;
36152             case 'wider': // used to expand cols on touch..
36153             case 'narrow':
36154                 var cw = cm.getColumnWidth(index);
36155                 cw += (item.id == 'wider' ? 1 : -1) * 50;
36156                 cw = Math.max(0, cw);
36157                 cw = Math.min(cw,4000);
36158                 cm.setColumnWidth(index, cw);
36159                 break;
36160                 
36161             default:
36162                 index = cm.getIndexById(item.id.substr(4));
36163                 if(index != -1){
36164                     if(item.checked && cm.getColumnCount(true) <= 1){
36165                         this.onDenyColumnHide();
36166                         return false;
36167                     }
36168                     cm.setHidden(index, item.checked);
36169                 }
36170         }
36171         return true;
36172     },
36173
36174     beforeColMenuShow : function(){
36175         var cm = this.cm,  colCount = cm.getColumnCount();
36176         this.colMenu.removeAll();
36177         for(var i = 0; i < colCount; i++){
36178             this.colMenu.add(new Roo.menu.CheckItem({
36179                 id: "col-"+cm.getColumnId(i),
36180                 text: cm.getColumnHeader(i),
36181                 checked: !cm.isHidden(i),
36182                 hideOnClick:false
36183             }));
36184         }
36185     },
36186
36187     handleHdCtx : function(g, index, e){
36188         e.stopEvent();
36189         var hd = this.getHeaderCell(index);
36190         this.hdCtxIndex = index;
36191         var ms = this.hmenu.items, cm = this.cm;
36192         ms.get("asc").setDisabled(!cm.isSortable(index));
36193         ms.get("desc").setDisabled(!cm.isSortable(index));
36194         if(this.grid.enableColLock !== false){
36195             ms.get("lock").setDisabled(cm.isLocked(index));
36196             ms.get("unlock").setDisabled(!cm.isLocked(index));
36197         }
36198         this.hmenu.show(hd, "tl-bl");
36199     },
36200
36201     handleHdOver : function(e){
36202         var hd = this.findHeaderCell(e.getTarget());
36203         if(hd && !this.headersDisabled){
36204             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36205                this.fly(hd).addClass("x-grid-hd-over");
36206             }
36207         }
36208     },
36209
36210     handleHdOut : function(e){
36211         var hd = this.findHeaderCell(e.getTarget());
36212         if(hd){
36213             this.fly(hd).removeClass("x-grid-hd-over");
36214         }
36215     },
36216
36217     handleSplitDblClick : function(e, t){
36218         var i = this.getCellIndex(t);
36219         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36220             this.autoSizeColumn(i, true);
36221             this.layout();
36222         }
36223     },
36224
36225     render : function(){
36226
36227         var cm = this.cm;
36228         var colCount = cm.getColumnCount();
36229
36230         if(this.grid.monitorWindowResize === true){
36231             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36232         }
36233         var header = this.renderHeaders();
36234         var body = this.templates.body.apply({rows:""});
36235         var html = this.templates.master.apply({
36236             lockedBody: body,
36237             body: body,
36238             lockedHeader: header[0],
36239             header: header[1]
36240         });
36241
36242         //this.updateColumns();
36243
36244         this.grid.getGridEl().dom.innerHTML = html;
36245
36246         this.initElements();
36247         
36248         // a kludge to fix the random scolling effect in webkit
36249         this.el.on("scroll", function() {
36250             this.el.dom.scrollTop=0; // hopefully not recursive..
36251         },this);
36252
36253         this.scroller.on("scroll", this.handleScroll, this);
36254         this.lockedBody.on("mousewheel", this.handleWheel, this);
36255         this.mainBody.on("mousewheel", this.handleWheel, this);
36256
36257         this.mainHd.on("mouseover", this.handleHdOver, this);
36258         this.mainHd.on("mouseout", this.handleHdOut, this);
36259         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36260                 {delegate: "."+this.splitClass});
36261
36262         this.lockedHd.on("mouseover", this.handleHdOver, this);
36263         this.lockedHd.on("mouseout", this.handleHdOut, this);
36264         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36265                 {delegate: "."+this.splitClass});
36266
36267         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36268             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36269         }
36270
36271         this.updateSplitters();
36272
36273         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36274             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36275             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36276         }
36277
36278         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36279             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36280             this.hmenu.add(
36281                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36282                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36283             );
36284             if(this.grid.enableColLock !== false){
36285                 this.hmenu.add('-',
36286                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36287                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36288                 );
36289             }
36290             if (Roo.isTouch) {
36291                  this.hmenu.add('-',
36292                     {id:"wider", text: this.columnsWiderText},
36293                     {id:"narrow", text: this.columnsNarrowText }
36294                 );
36295                 
36296                  
36297             }
36298             
36299             if(this.grid.enableColumnHide !== false){
36300
36301                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36302                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36303                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36304
36305                 this.hmenu.add('-',
36306                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36307                 );
36308             }
36309             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36310
36311             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36312         }
36313
36314         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36315             this.dd = new Roo.grid.GridDragZone(this.grid, {
36316                 ddGroup : this.grid.ddGroup || 'GridDD'
36317             });
36318             
36319         }
36320
36321         /*
36322         for(var i = 0; i < colCount; i++){
36323             if(cm.isHidden(i)){
36324                 this.hideColumn(i);
36325             }
36326             if(cm.config[i].align){
36327                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36328                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36329             }
36330         }*/
36331         
36332         this.updateHeaderSortState();
36333
36334         this.beforeInitialResize();
36335         this.layout(true);
36336
36337         // two part rendering gives faster view to the user
36338         this.renderPhase2.defer(1, this);
36339     },
36340
36341     renderPhase2 : function(){
36342         // render the rows now
36343         this.refresh();
36344         if(this.grid.autoSizeColumns){
36345             this.autoSizeColumns();
36346         }
36347     },
36348
36349     beforeInitialResize : function(){
36350
36351     },
36352
36353     onColumnSplitterMoved : function(i, w){
36354         this.userResized = true;
36355         var cm = this.grid.colModel;
36356         cm.setColumnWidth(i, w, true);
36357         var cid = cm.getColumnId(i);
36358         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36359         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36360         this.updateSplitters();
36361         this.layout();
36362         this.grid.fireEvent("columnresize", i, w);
36363     },
36364
36365     syncRowHeights : function(startIndex, endIndex){
36366         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36367             startIndex = startIndex || 0;
36368             var mrows = this.getBodyTable().rows;
36369             var lrows = this.getLockedTable().rows;
36370             var len = mrows.length-1;
36371             endIndex = Math.min(endIndex || len, len);
36372             for(var i = startIndex; i <= endIndex; i++){
36373                 var m = mrows[i], l = lrows[i];
36374                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36375                 m.style.height = l.style.height = h + "px";
36376             }
36377         }
36378     },
36379
36380     layout : function(initialRender, is2ndPass)
36381     {
36382         var g = this.grid;
36383         var auto = g.autoHeight;
36384         var scrollOffset = 16;
36385         var c = g.getGridEl(), cm = this.cm,
36386                 expandCol = g.autoExpandColumn,
36387                 gv = this;
36388         //c.beginMeasure();
36389
36390         if(!c.dom.offsetWidth){ // display:none?
36391             if(initialRender){
36392                 this.lockedWrap.show();
36393                 this.mainWrap.show();
36394             }
36395             return;
36396         }
36397
36398         var hasLock = this.cm.isLocked(0);
36399
36400         var tbh = this.headerPanel.getHeight();
36401         var bbh = this.footerPanel.getHeight();
36402
36403         if(auto){
36404             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36405             var newHeight = ch + c.getBorderWidth("tb");
36406             if(g.maxHeight){
36407                 newHeight = Math.min(g.maxHeight, newHeight);
36408             }
36409             c.setHeight(newHeight);
36410         }
36411
36412         if(g.autoWidth){
36413             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36414         }
36415
36416         var s = this.scroller;
36417
36418         var csize = c.getSize(true);
36419
36420         this.el.setSize(csize.width, csize.height);
36421
36422         this.headerPanel.setWidth(csize.width);
36423         this.footerPanel.setWidth(csize.width);
36424
36425         var hdHeight = this.mainHd.getHeight();
36426         var vw = csize.width;
36427         var vh = csize.height - (tbh + bbh);
36428
36429         s.setSize(vw, vh);
36430
36431         var bt = this.getBodyTable();
36432         
36433         if(cm.getLockedCount() == cm.config.length){
36434             bt = this.getLockedTable();
36435         }
36436         
36437         var ltWidth = hasLock ?
36438                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36439
36440         var scrollHeight = bt.offsetHeight;
36441         var scrollWidth = ltWidth + bt.offsetWidth;
36442         var vscroll = false, hscroll = false;
36443
36444         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36445
36446         var lw = this.lockedWrap, mw = this.mainWrap;
36447         var lb = this.lockedBody, mb = this.mainBody;
36448
36449         setTimeout(function(){
36450             var t = s.dom.offsetTop;
36451             var w = s.dom.clientWidth,
36452                 h = s.dom.clientHeight;
36453
36454             lw.setTop(t);
36455             lw.setSize(ltWidth, h);
36456
36457             mw.setLeftTop(ltWidth, t);
36458             mw.setSize(w-ltWidth, h);
36459
36460             lb.setHeight(h-hdHeight);
36461             mb.setHeight(h-hdHeight);
36462
36463             if(is2ndPass !== true && !gv.userResized && expandCol){
36464                 // high speed resize without full column calculation
36465                 
36466                 var ci = cm.getIndexById(expandCol);
36467                 if (ci < 0) {
36468                     ci = cm.findColumnIndex(expandCol);
36469                 }
36470                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36471                 var expandId = cm.getColumnId(ci);
36472                 var  tw = cm.getTotalWidth(false);
36473                 var currentWidth = cm.getColumnWidth(ci);
36474                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36475                 if(currentWidth != cw){
36476                     cm.setColumnWidth(ci, cw, true);
36477                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36478                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36479                     gv.updateSplitters();
36480                     gv.layout(false, true);
36481                 }
36482             }
36483
36484             if(initialRender){
36485                 lw.show();
36486                 mw.show();
36487             }
36488             //c.endMeasure();
36489         }, 10);
36490     },
36491
36492     onWindowResize : function(){
36493         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36494             return;
36495         }
36496         this.layout();
36497     },
36498
36499     appendFooter : function(parentEl){
36500         return null;
36501     },
36502
36503     sortAscText : "Sort Ascending",
36504     sortDescText : "Sort Descending",
36505     lockText : "Lock Column",
36506     unlockText : "Unlock Column",
36507     columnsText : "Columns",
36508  
36509     columnsWiderText : "Wider",
36510     columnsNarrowText : "Thinner"
36511 });
36512
36513
36514 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36515     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36516     this.proxy.el.addClass('x-grid3-col-dd');
36517 };
36518
36519 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36520     handleMouseDown : function(e){
36521
36522     },
36523
36524     callHandleMouseDown : function(e){
36525         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36526     }
36527 });
36528 /*
36529  * Based on:
36530  * Ext JS Library 1.1.1
36531  * Copyright(c) 2006-2007, Ext JS, LLC.
36532  *
36533  * Originally Released Under LGPL - original licence link has changed is not relivant.
36534  *
36535  * Fork - LGPL
36536  * <script type="text/javascript">
36537  */
36538  /**
36539  * @extends Roo.dd.DDProxy
36540  * @class Roo.grid.SplitDragZone
36541  * Support for Column Header resizing
36542  * @constructor
36543  * @param {Object} config
36544  */
36545 // private
36546 // This is a support class used internally by the Grid components
36547 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36548     this.grid = grid;
36549     this.view = grid.getView();
36550     this.proxy = this.view.resizeProxy;
36551     Roo.grid.SplitDragZone.superclass.constructor.call(
36552         this,
36553         hd, // ID
36554         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
36555         {  // CONFIG
36556             dragElId : Roo.id(this.proxy.dom),
36557             resizeFrame:false
36558         }
36559     );
36560     
36561     this.setHandleElId(Roo.id(hd));
36562     if (hd2 !== false) {
36563         this.setOuterHandleElId(Roo.id(hd2));
36564     }
36565     
36566     this.scroll = false;
36567 };
36568 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36569     fly: Roo.Element.fly,
36570
36571     b4StartDrag : function(x, y){
36572         this.view.headersDisabled = true;
36573         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
36574                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
36575         );
36576         this.proxy.setHeight(h);
36577         
36578         // for old system colWidth really stored the actual width?
36579         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
36580         // which in reality did not work.. - it worked only for fixed sizes
36581         // for resizable we need to use actual sizes.
36582         var w = this.cm.getColumnWidth(this.cellIndex);
36583         if (!this.view.mainWrap) {
36584             // bootstrap.
36585             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
36586         }
36587         
36588         
36589         
36590         // this was w-this.grid.minColumnWidth;
36591         // doesnt really make sense? - w = thie curren width or the rendered one?
36592         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36593         this.resetConstraints();
36594         this.setXConstraint(minw, 1000);
36595         this.setYConstraint(0, 0);
36596         this.minX = x - minw;
36597         this.maxX = x + 1000;
36598         this.startPos = x;
36599         if (!this.view.mainWrap) { // this is Bootstrap code..
36600             this.getDragEl().style.display='block';
36601         }
36602         
36603         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36604     },
36605
36606
36607     handleMouseDown : function(e){
36608         ev = Roo.EventObject.setEvent(e);
36609         var t = this.fly(ev.getTarget());
36610         if(t.hasClass("x-grid-split")){
36611             this.cellIndex = this.view.getCellIndex(t.dom);
36612             this.split = t.dom;
36613             this.cm = this.grid.colModel;
36614             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36615                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36616             }
36617         }
36618     },
36619
36620     endDrag : function(e){
36621         this.view.headersDisabled = false;
36622         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36623         var diff = endX - this.startPos;
36624         // 
36625         var w = this.cm.getColumnWidth(this.cellIndex);
36626         if (!this.view.mainWrap) {
36627             w = 0;
36628         }
36629         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
36630     },
36631
36632     autoOffset : function(){
36633         this.setDelta(0,0);
36634     }
36635 });/*
36636  * Based on:
36637  * Ext JS Library 1.1.1
36638  * Copyright(c) 2006-2007, Ext JS, LLC.
36639  *
36640  * Originally Released Under LGPL - original licence link has changed is not relivant.
36641  *
36642  * Fork - LGPL
36643  * <script type="text/javascript">
36644  */
36645  
36646 // private
36647 // This is a support class used internally by the Grid components
36648 Roo.grid.GridDragZone = function(grid, config){
36649     this.view = grid.getView();
36650     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36651     if(this.view.lockedBody){
36652         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36653         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36654     }
36655     this.scroll = false;
36656     this.grid = grid;
36657     this.ddel = document.createElement('div');
36658     this.ddel.className = 'x-grid-dd-wrap';
36659 };
36660
36661 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36662     ddGroup : "GridDD",
36663
36664     getDragData : function(e){
36665         var t = Roo.lib.Event.getTarget(e);
36666         var rowIndex = this.view.findRowIndex(t);
36667         var sm = this.grid.selModel;
36668             
36669         //Roo.log(rowIndex);
36670         
36671         if (sm.getSelectedCell) {
36672             // cell selection..
36673             if (!sm.getSelectedCell()) {
36674                 return false;
36675             }
36676             if (rowIndex != sm.getSelectedCell()[0]) {
36677                 return false;
36678             }
36679         
36680         }
36681         if (sm.getSelections && sm.getSelections().length < 1) {
36682             return false;
36683         }
36684         
36685         
36686         // before it used to all dragging of unseleted... - now we dont do that.
36687         if(rowIndex !== false){
36688             
36689             // if editorgrid.. 
36690             
36691             
36692             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
36693                
36694             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36695               //  
36696             //}
36697             if (e.hasModifier()){
36698                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36699             }
36700             
36701             Roo.log("getDragData");
36702             
36703             return {
36704                 grid: this.grid,
36705                 ddel: this.ddel,
36706                 rowIndex: rowIndex,
36707                 selections: sm.getSelections ? sm.getSelections() : (
36708                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
36709             };
36710         }
36711         return false;
36712     },
36713     
36714     
36715     onInitDrag : function(e){
36716         var data = this.dragData;
36717         this.ddel.innerHTML = this.grid.getDragDropText();
36718         this.proxy.update(this.ddel);
36719         // fire start drag?
36720     },
36721
36722     afterRepair : function(){
36723         this.dragging = false;
36724     },
36725
36726     getRepairXY : function(e, data){
36727         return false;
36728     },
36729
36730     onEndDrag : function(data, e){
36731         // fire end drag?
36732     },
36733
36734     onValidDrop : function(dd, e, id){
36735         // fire drag drop?
36736         this.hideProxy();
36737     },
36738
36739     beforeInvalidDrop : function(e, id){
36740
36741     }
36742 });/*
36743  * Based on:
36744  * Ext JS Library 1.1.1
36745  * Copyright(c) 2006-2007, Ext JS, LLC.
36746  *
36747  * Originally Released Under LGPL - original licence link has changed is not relivant.
36748  *
36749  * Fork - LGPL
36750  * <script type="text/javascript">
36751  */
36752  
36753
36754 /**
36755  * @class Roo.grid.ColumnModel
36756  * @extends Roo.util.Observable
36757  * This is the default implementation of a ColumnModel used by the Grid. It defines
36758  * the columns in the grid.
36759  * <br>Usage:<br>
36760  <pre><code>
36761  var colModel = new Roo.grid.ColumnModel([
36762         {header: "Ticker", width: 60, sortable: true, locked: true},
36763         {header: "Company Name", width: 150, sortable: true},
36764         {header: "Market Cap.", width: 100, sortable: true},
36765         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36766         {header: "Employees", width: 100, sortable: true, resizable: false}
36767  ]);
36768  </code></pre>
36769  * <p>
36770  
36771  * The config options listed for this class are options which may appear in each
36772  * individual column definition.
36773  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36774  * @constructor
36775  * @param {Object} config An Array of column config objects. See this class's
36776  * config objects for details.
36777 */
36778 Roo.grid.ColumnModel = function(config){
36779         /**
36780      * The config passed into the constructor
36781      */
36782     this.config = []; //config;
36783     this.lookup = {};
36784
36785     // if no id, create one
36786     // if the column does not have a dataIndex mapping,
36787     // map it to the order it is in the config
36788     for(var i = 0, len = config.length; i < len; i++){
36789         this.addColumn(config[i]);
36790         
36791     }
36792
36793     /**
36794      * The width of columns which have no width specified (defaults to 100)
36795      * @type Number
36796      */
36797     this.defaultWidth = 100;
36798
36799     /**
36800      * Default sortable of columns which have no sortable specified (defaults to false)
36801      * @type Boolean
36802      */
36803     this.defaultSortable = false;
36804
36805     this.addEvents({
36806         /**
36807              * @event widthchange
36808              * Fires when the width of a column changes.
36809              * @param {ColumnModel} this
36810              * @param {Number} columnIndex The column index
36811              * @param {Number} newWidth The new width
36812              */
36813             "widthchange": true,
36814         /**
36815              * @event headerchange
36816              * Fires when the text of a header changes.
36817              * @param {ColumnModel} this
36818              * @param {Number} columnIndex The column index
36819              * @param {Number} newText The new header text
36820              */
36821             "headerchange": true,
36822         /**
36823              * @event hiddenchange
36824              * Fires when a column is hidden or "unhidden".
36825              * @param {ColumnModel} this
36826              * @param {Number} columnIndex The column index
36827              * @param {Boolean} hidden true if hidden, false otherwise
36828              */
36829             "hiddenchange": true,
36830             /**
36831          * @event columnmoved
36832          * Fires when a column is moved.
36833          * @param {ColumnModel} this
36834          * @param {Number} oldIndex
36835          * @param {Number} newIndex
36836          */
36837         "columnmoved" : true,
36838         /**
36839          * @event columlockchange
36840          * Fires when a column's locked state is changed
36841          * @param {ColumnModel} this
36842          * @param {Number} colIndex
36843          * @param {Boolean} locked true if locked
36844          */
36845         "columnlockchange" : true
36846     });
36847     Roo.grid.ColumnModel.superclass.constructor.call(this);
36848 };
36849 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36850     /**
36851      * @cfg {String} header The header text to display in the Grid view.
36852      */
36853         /**
36854      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
36855      */
36856         /**
36857      * @cfg {String} smHeader Header at Bootsrap Small width
36858      */
36859         /**
36860      * @cfg {String} mdHeader Header at Bootsrap Medium width
36861      */
36862         /**
36863      * @cfg {String} lgHeader Header at Bootsrap Large width
36864      */
36865         /**
36866      * @cfg {String} xlHeader Header at Bootsrap extra Large width
36867      */
36868     /**
36869      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36870      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36871      * specified, the column's index is used as an index into the Record's data Array.
36872      */
36873     /**
36874      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36875      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36876      */
36877     /**
36878      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36879      * Defaults to the value of the {@link #defaultSortable} property.
36880      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36881      */
36882     /**
36883      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36884      */
36885     /**
36886      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36887      */
36888     /**
36889      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36890      */
36891     /**
36892      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36893      */
36894     /**
36895      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36896      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36897      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
36898      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
36899      */
36900        /**
36901      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36902      */
36903     /**
36904      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36905      */
36906     /**
36907      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
36908      */
36909     /**
36910      * @cfg {String} cursor (Optional)
36911      */
36912     /**
36913      * @cfg {String} tooltip (Optional)
36914      */
36915     /**
36916      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
36917      */
36918     /**
36919      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
36920      */
36921     /**
36922      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
36923      */
36924     /**
36925      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
36926      */
36927         /**
36928      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
36929      */
36930     /**
36931      * Returns the id of the column at the specified index.
36932      * @param {Number} index The column index
36933      * @return {String} the id
36934      */
36935     getColumnId : function(index){
36936         return this.config[index].id;
36937     },
36938
36939     /**
36940      * Returns the column for a specified id.
36941      * @param {String} id The column id
36942      * @return {Object} the column
36943      */
36944     getColumnById : function(id){
36945         return this.lookup[id];
36946     },
36947
36948     
36949     /**
36950      * Returns the column Object for a specified dataIndex.
36951      * @param {String} dataIndex The column dataIndex
36952      * @return {Object|Boolean} the column or false if not found
36953      */
36954     getColumnByDataIndex: function(dataIndex){
36955         var index = this.findColumnIndex(dataIndex);
36956         return index > -1 ? this.config[index] : false;
36957     },
36958     
36959     /**
36960      * Returns the index for a specified column id.
36961      * @param {String} id The column id
36962      * @return {Number} the index, or -1 if not found
36963      */
36964     getIndexById : function(id){
36965         for(var i = 0, len = this.config.length; i < len; i++){
36966             if(this.config[i].id == id){
36967                 return i;
36968             }
36969         }
36970         return -1;
36971     },
36972     
36973     /**
36974      * Returns the index for a specified column dataIndex.
36975      * @param {String} dataIndex The column dataIndex
36976      * @return {Number} the index, or -1 if not found
36977      */
36978     
36979     findColumnIndex : function(dataIndex){
36980         for(var i = 0, len = this.config.length; i < len; i++){
36981             if(this.config[i].dataIndex == dataIndex){
36982                 return i;
36983             }
36984         }
36985         return -1;
36986     },
36987     
36988     
36989     moveColumn : function(oldIndex, newIndex){
36990         var c = this.config[oldIndex];
36991         this.config.splice(oldIndex, 1);
36992         this.config.splice(newIndex, 0, c);
36993         this.dataMap = null;
36994         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36995     },
36996
36997     isLocked : function(colIndex){
36998         return this.config[colIndex].locked === true;
36999     },
37000
37001     setLocked : function(colIndex, value, suppressEvent){
37002         if(this.isLocked(colIndex) == value){
37003             return;
37004         }
37005         this.config[colIndex].locked = value;
37006         if(!suppressEvent){
37007             this.fireEvent("columnlockchange", this, colIndex, value);
37008         }
37009     },
37010
37011     getTotalLockedWidth : function(){
37012         var totalWidth = 0;
37013         for(var i = 0; i < this.config.length; i++){
37014             if(this.isLocked(i) && !this.isHidden(i)){
37015                 this.totalWidth += this.getColumnWidth(i);
37016             }
37017         }
37018         return totalWidth;
37019     },
37020
37021     getLockedCount : function(){
37022         for(var i = 0, len = this.config.length; i < len; i++){
37023             if(!this.isLocked(i)){
37024                 return i;
37025             }
37026         }
37027         
37028         return this.config.length;
37029     },
37030
37031     /**
37032      * Returns the number of columns.
37033      * @return {Number}
37034      */
37035     getColumnCount : function(visibleOnly){
37036         if(visibleOnly === true){
37037             var c = 0;
37038             for(var i = 0, len = this.config.length; i < len; i++){
37039                 if(!this.isHidden(i)){
37040                     c++;
37041                 }
37042             }
37043             return c;
37044         }
37045         return this.config.length;
37046     },
37047
37048     /**
37049      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37050      * @param {Function} fn
37051      * @param {Object} scope (optional)
37052      * @return {Array} result
37053      */
37054     getColumnsBy : function(fn, scope){
37055         var r = [];
37056         for(var i = 0, len = this.config.length; i < len; i++){
37057             var c = this.config[i];
37058             if(fn.call(scope||this, c, i) === true){
37059                 r[r.length] = c;
37060             }
37061         }
37062         return r;
37063     },
37064
37065     /**
37066      * Returns true if the specified column is sortable.
37067      * @param {Number} col The column index
37068      * @return {Boolean}
37069      */
37070     isSortable : function(col){
37071         if(typeof this.config[col].sortable == "undefined"){
37072             return this.defaultSortable;
37073         }
37074         return this.config[col].sortable;
37075     },
37076
37077     /**
37078      * Returns the rendering (formatting) function defined for the column.
37079      * @param {Number} col The column index.
37080      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37081      */
37082     getRenderer : function(col){
37083         if(!this.config[col].renderer){
37084             return Roo.grid.ColumnModel.defaultRenderer;
37085         }
37086         return this.config[col].renderer;
37087     },
37088
37089     /**
37090      * Sets the rendering (formatting) function for a column.
37091      * @param {Number} col The column index
37092      * @param {Function} fn The function to use to process the cell's raw data
37093      * to return HTML markup for the grid view. The render function is called with
37094      * the following parameters:<ul>
37095      * <li>Data value.</li>
37096      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37097      * <li>css A CSS style string to apply to the table cell.</li>
37098      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37099      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37100      * <li>Row index</li>
37101      * <li>Column index</li>
37102      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37103      */
37104     setRenderer : function(col, fn){
37105         this.config[col].renderer = fn;
37106     },
37107
37108     /**
37109      * Returns the width for the specified column.
37110      * @param {Number} col The column index
37111      * @param (optional) {String} gridSize bootstrap width size.
37112      * @return {Number}
37113      */
37114     getColumnWidth : function(col, gridSize)
37115         {
37116                 var cfg = this.config[col];
37117                 
37118                 if (typeof(gridSize) == 'undefined') {
37119                         return cfg.width * 1 || this.defaultWidth;
37120                 }
37121                 if (gridSize === false) { // if we set it..
37122                         return cfg.width || false;
37123                 }
37124                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
37125                 
37126                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
37127                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
37128                                 continue;
37129                         }
37130                         return cfg[ sizes[i] ];
37131                 }
37132                 return 1;
37133                 
37134     },
37135
37136     /**
37137      * Sets the width for a column.
37138      * @param {Number} col The column index
37139      * @param {Number} width The new width
37140      */
37141     setColumnWidth : function(col, width, suppressEvent){
37142         this.config[col].width = width;
37143         this.totalWidth = null;
37144         if(!suppressEvent){
37145              this.fireEvent("widthchange", this, col, width);
37146         }
37147     },
37148
37149     /**
37150      * Returns the total width of all columns.
37151      * @param {Boolean} includeHidden True to include hidden column widths
37152      * @return {Number}
37153      */
37154     getTotalWidth : function(includeHidden){
37155         if(!this.totalWidth){
37156             this.totalWidth = 0;
37157             for(var i = 0, len = this.config.length; i < len; i++){
37158                 if(includeHidden || !this.isHidden(i)){
37159                     this.totalWidth += this.getColumnWidth(i);
37160                 }
37161             }
37162         }
37163         return this.totalWidth;
37164     },
37165
37166     /**
37167      * Returns the header for the specified column.
37168      * @param {Number} col The column index
37169      * @return {String}
37170      */
37171     getColumnHeader : function(col){
37172         return this.config[col].header;
37173     },
37174
37175     /**
37176      * Sets the header for a column.
37177      * @param {Number} col The column index
37178      * @param {String} header The new header
37179      */
37180     setColumnHeader : function(col, header){
37181         this.config[col].header = header;
37182         this.fireEvent("headerchange", this, col, header);
37183     },
37184
37185     /**
37186      * Returns the tooltip for the specified column.
37187      * @param {Number} col The column index
37188      * @return {String}
37189      */
37190     getColumnTooltip : function(col){
37191             return this.config[col].tooltip;
37192     },
37193     /**
37194      * Sets the tooltip for a column.
37195      * @param {Number} col The column index
37196      * @param {String} tooltip The new tooltip
37197      */
37198     setColumnTooltip : function(col, tooltip){
37199             this.config[col].tooltip = tooltip;
37200     },
37201
37202     /**
37203      * Returns the dataIndex for the specified column.
37204      * @param {Number} col The column index
37205      * @return {Number}
37206      */
37207     getDataIndex : function(col){
37208         return this.config[col].dataIndex;
37209     },
37210
37211     /**
37212      * Sets the dataIndex for a column.
37213      * @param {Number} col The column index
37214      * @param {Number} dataIndex The new dataIndex
37215      */
37216     setDataIndex : function(col, dataIndex){
37217         this.config[col].dataIndex = dataIndex;
37218     },
37219
37220     
37221     
37222     /**
37223      * Returns true if the cell is editable.
37224      * @param {Number} colIndex The column index
37225      * @param {Number} rowIndex The row index - this is nto actually used..?
37226      * @return {Boolean}
37227      */
37228     isCellEditable : function(colIndex, rowIndex){
37229         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37230     },
37231
37232     /**
37233      * Returns the editor defined for the cell/column.
37234      * return false or null to disable editing.
37235      * @param {Number} colIndex The column index
37236      * @param {Number} rowIndex The row index
37237      * @return {Object}
37238      */
37239     getCellEditor : function(colIndex, rowIndex){
37240         return this.config[colIndex].editor;
37241     },
37242
37243     /**
37244      * Sets if a column is editable.
37245      * @param {Number} col The column index
37246      * @param {Boolean} editable True if the column is editable
37247      */
37248     setEditable : function(col, editable){
37249         this.config[col].editable = editable;
37250     },
37251
37252
37253     /**
37254      * Returns true if the column is hidden.
37255      * @param {Number} colIndex The column index
37256      * @return {Boolean}
37257      */
37258     isHidden : function(colIndex){
37259         return this.config[colIndex].hidden;
37260     },
37261
37262
37263     /**
37264      * Returns true if the column width cannot be changed
37265      */
37266     isFixed : function(colIndex){
37267         return this.config[colIndex].fixed;
37268     },
37269
37270     /**
37271      * Returns true if the column can be resized
37272      * @return {Boolean}
37273      */
37274     isResizable : function(colIndex){
37275         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37276     },
37277     /**
37278      * Sets if a column is hidden.
37279      * @param {Number} colIndex The column index
37280      * @param {Boolean} hidden True if the column is hidden
37281      */
37282     setHidden : function(colIndex, hidden){
37283         this.config[colIndex].hidden = hidden;
37284         this.totalWidth = null;
37285         this.fireEvent("hiddenchange", this, colIndex, hidden);
37286     },
37287
37288     /**
37289      * Sets the editor for a column.
37290      * @param {Number} col The column index
37291      * @param {Object} editor The editor object
37292      */
37293     setEditor : function(col, editor){
37294         this.config[col].editor = editor;
37295     },
37296     /**
37297      * Add a column (experimental...) - defaults to adding to the end..
37298      * @param {Object} config 
37299     */
37300     addColumn : function(c)
37301     {
37302     
37303         var i = this.config.length;
37304         this.config[i] = c;
37305         
37306         if(typeof c.dataIndex == "undefined"){
37307             c.dataIndex = i;
37308         }
37309         if(typeof c.renderer == "string"){
37310             c.renderer = Roo.util.Format[c.renderer];
37311         }
37312         if(typeof c.id == "undefined"){
37313             c.id = Roo.id();
37314         }
37315         if(c.editor && c.editor.xtype){
37316             c.editor  = Roo.factory(c.editor, Roo.grid);
37317         }
37318         if(c.editor && c.editor.isFormField){
37319             c.editor = new Roo.grid.GridEditor(c.editor);
37320         }
37321         this.lookup[c.id] = c;
37322     }
37323     
37324 });
37325
37326 Roo.grid.ColumnModel.defaultRenderer = function(value)
37327 {
37328     if(typeof value == "object") {
37329         return value;
37330     }
37331         if(typeof value == "string" && value.length < 1){
37332             return "&#160;";
37333         }
37334     
37335         return String.format("{0}", value);
37336 };
37337
37338 // Alias for backwards compatibility
37339 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37340 /*
37341  * Based on:
37342  * Ext JS Library 1.1.1
37343  * Copyright(c) 2006-2007, Ext JS, LLC.
37344  *
37345  * Originally Released Under LGPL - original licence link has changed is not relivant.
37346  *
37347  * Fork - LGPL
37348  * <script type="text/javascript">
37349  */
37350
37351 /**
37352  * @class Roo.grid.AbstractSelectionModel
37353  * @extends Roo.util.Observable
37354  * @abstract
37355  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37356  * implemented by descendant classes.  This class should not be directly instantiated.
37357  * @constructor
37358  */
37359 Roo.grid.AbstractSelectionModel = function(){
37360     this.locked = false;
37361     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37362 };
37363
37364 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37365     /** @ignore Called by the grid automatically. Do not call directly. */
37366     init : function(grid){
37367         this.grid = grid;
37368         this.initEvents();
37369     },
37370
37371     /**
37372      * Locks the selections.
37373      */
37374     lock : function(){
37375         this.locked = true;
37376     },
37377
37378     /**
37379      * Unlocks the selections.
37380      */
37381     unlock : function(){
37382         this.locked = false;
37383     },
37384
37385     /**
37386      * Returns true if the selections are locked.
37387      * @return {Boolean}
37388      */
37389     isLocked : function(){
37390         return this.locked;
37391     }
37392 });/*
37393  * Based on:
37394  * Ext JS Library 1.1.1
37395  * Copyright(c) 2006-2007, Ext JS, LLC.
37396  *
37397  * Originally Released Under LGPL - original licence link has changed is not relivant.
37398  *
37399  * Fork - LGPL
37400  * <script type="text/javascript">
37401  */
37402 /**
37403  * @extends Roo.grid.AbstractSelectionModel
37404  * @class Roo.grid.RowSelectionModel
37405  * The default SelectionModel used by {@link Roo.grid.Grid}.
37406  * It supports multiple selections and keyboard selection/navigation. 
37407  * @constructor
37408  * @param {Object} config
37409  */
37410 Roo.grid.RowSelectionModel = function(config){
37411     Roo.apply(this, config);
37412     this.selections = new Roo.util.MixedCollection(false, function(o){
37413         return o.id;
37414     });
37415
37416     this.last = false;
37417     this.lastActive = false;
37418
37419     this.addEvents({
37420         /**
37421         * @event selectionchange
37422         * Fires when the selection changes
37423         * @param {SelectionModel} this
37424         */
37425        "selectionchange" : true,
37426        /**
37427         * @event afterselectionchange
37428         * Fires after the selection changes (eg. by key press or clicking)
37429         * @param {SelectionModel} this
37430         */
37431        "afterselectionchange" : true,
37432        /**
37433         * @event beforerowselect
37434         * Fires when a row is selected being selected, return false to cancel.
37435         * @param {SelectionModel} this
37436         * @param {Number} rowIndex The selected index
37437         * @param {Boolean} keepExisting False if other selections will be cleared
37438         */
37439        "beforerowselect" : true,
37440        /**
37441         * @event rowselect
37442         * Fires when a row is selected.
37443         * @param {SelectionModel} this
37444         * @param {Number} rowIndex The selected index
37445         * @param {Roo.data.Record} r The record
37446         */
37447        "rowselect" : true,
37448        /**
37449         * @event rowdeselect
37450         * Fires when a row is deselected.
37451         * @param {SelectionModel} this
37452         * @param {Number} rowIndex The selected index
37453         */
37454         "rowdeselect" : true
37455     });
37456     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37457     this.locked = false;
37458 };
37459
37460 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37461     /**
37462      * @cfg {Boolean} singleSelect
37463      * True to allow selection of only one row at a time (defaults to false)
37464      */
37465     singleSelect : false,
37466
37467     // private
37468     initEvents : function(){
37469
37470         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37471             this.grid.on("mousedown", this.handleMouseDown, this);
37472         }else{ // allow click to work like normal
37473             this.grid.on("rowclick", this.handleDragableRowClick, this);
37474         }
37475         // bootstrap does not have a view..
37476         var view = this.grid.view ? this.grid.view : this.grid;
37477         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37478             "up" : function(e){
37479                 if(!e.shiftKey){
37480                     this.selectPrevious(e.shiftKey);
37481                 }else if(this.last !== false && this.lastActive !== false){
37482                     var last = this.last;
37483                     this.selectRange(this.last,  this.lastActive-1);
37484                     view.focusRow(this.lastActive);
37485                     if(last !== false){
37486                         this.last = last;
37487                     }
37488                 }else{
37489                     this.selectFirstRow();
37490                 }
37491                 this.fireEvent("afterselectionchange", this);
37492             },
37493             "down" : function(e){
37494                 if(!e.shiftKey){
37495                     this.selectNext(e.shiftKey);
37496                 }else if(this.last !== false && this.lastActive !== false){
37497                     var last = this.last;
37498                     this.selectRange(this.last,  this.lastActive+1);
37499                     view.focusRow(this.lastActive);
37500                     if(last !== false){
37501                         this.last = last;
37502                     }
37503                 }else{
37504                     this.selectFirstRow();
37505                 }
37506                 this.fireEvent("afterselectionchange", this);
37507             },
37508             scope: this
37509         });
37510
37511          
37512         view.on("refresh", this.onRefresh, this);
37513         view.on("rowupdated", this.onRowUpdated, this);
37514         view.on("rowremoved", this.onRemove, this);
37515     },
37516
37517     // private
37518     onRefresh : function(){
37519         var ds = this.grid.ds, i, v = this.grid.view;
37520         var s = this.selections;
37521         s.each(function(r){
37522             if((i = ds.indexOfId(r.id)) != -1){
37523                 v.onRowSelect(i);
37524                 s.add(ds.getAt(i)); // updating the selection relate data
37525             }else{
37526                 s.remove(r);
37527             }
37528         });
37529     },
37530
37531     // private
37532     onRemove : function(v, index, r){
37533         this.selections.remove(r);
37534     },
37535
37536     // private
37537     onRowUpdated : function(v, index, r){
37538         if(this.isSelected(r)){
37539             v.onRowSelect(index);
37540         }
37541     },
37542
37543     /**
37544      * Select records.
37545      * @param {Array} records The records to select
37546      * @param {Boolean} keepExisting (optional) True to keep existing selections
37547      */
37548     selectRecords : function(records, keepExisting){
37549         if(!keepExisting){
37550             this.clearSelections();
37551         }
37552         var ds = this.grid.ds;
37553         for(var i = 0, len = records.length; i < len; i++){
37554             this.selectRow(ds.indexOf(records[i]), true);
37555         }
37556     },
37557
37558     /**
37559      * Gets the number of selected rows.
37560      * @return {Number}
37561      */
37562     getCount : function(){
37563         return this.selections.length;
37564     },
37565
37566     /**
37567      * Selects the first row in the grid.
37568      */
37569     selectFirstRow : function(){
37570         this.selectRow(0);
37571     },
37572
37573     /**
37574      * Select the last row.
37575      * @param {Boolean} keepExisting (optional) True to keep existing selections
37576      */
37577     selectLastRow : function(keepExisting){
37578         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
37579     },
37580
37581     /**
37582      * Selects the row immediately following the last selected row.
37583      * @param {Boolean} keepExisting (optional) True to keep existing selections
37584      */
37585     selectNext : function(keepExisting){
37586         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
37587             this.selectRow(this.last+1, keepExisting);
37588             var view = this.grid.view ? this.grid.view : this.grid;
37589             view.focusRow(this.last);
37590         }
37591     },
37592
37593     /**
37594      * Selects the row that precedes the last selected row.
37595      * @param {Boolean} keepExisting (optional) True to keep existing selections
37596      */
37597     selectPrevious : function(keepExisting){
37598         if(this.last){
37599             this.selectRow(this.last-1, keepExisting);
37600             var view = this.grid.view ? this.grid.view : this.grid;
37601             view.focusRow(this.last);
37602         }
37603     },
37604
37605     /**
37606      * Returns the selected records
37607      * @return {Array} Array of selected records
37608      */
37609     getSelections : function(){
37610         return [].concat(this.selections.items);
37611     },
37612
37613     /**
37614      * Returns the first selected record.
37615      * @return {Record}
37616      */
37617     getSelected : function(){
37618         return this.selections.itemAt(0);
37619     },
37620
37621
37622     /**
37623      * Clears all selections.
37624      */
37625     clearSelections : function(fast){
37626         if(this.locked) {
37627             return;
37628         }
37629         if(fast !== true){
37630             var ds = this.grid.ds;
37631             var s = this.selections;
37632             s.each(function(r){
37633                 this.deselectRow(ds.indexOfId(r.id));
37634             }, this);
37635             s.clear();
37636         }else{
37637             this.selections.clear();
37638         }
37639         this.last = false;
37640     },
37641
37642
37643     /**
37644      * Selects all rows.
37645      */
37646     selectAll : function(){
37647         if(this.locked) {
37648             return;
37649         }
37650         this.selections.clear();
37651         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
37652             this.selectRow(i, true);
37653         }
37654     },
37655
37656     /**
37657      * Returns True if there is a selection.
37658      * @return {Boolean}
37659      */
37660     hasSelection : function(){
37661         return this.selections.length > 0;
37662     },
37663
37664     /**
37665      * Returns True if the specified row is selected.
37666      * @param {Number/Record} record The record or index of the record to check
37667      * @return {Boolean}
37668      */
37669     isSelected : function(index){
37670         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
37671         return (r && this.selections.key(r.id) ? true : false);
37672     },
37673
37674     /**
37675      * Returns True if the specified record id is selected.
37676      * @param {String} id The id of record to check
37677      * @return {Boolean}
37678      */
37679     isIdSelected : function(id){
37680         return (this.selections.key(id) ? true : false);
37681     },
37682
37683     // private
37684     handleMouseDown : function(e, t)
37685     {
37686         var view = this.grid.view ? this.grid.view : this.grid;
37687         var rowIndex;
37688         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37689             return;
37690         };
37691         if(e.shiftKey && this.last !== false){
37692             var last = this.last;
37693             this.selectRange(last, rowIndex, e.ctrlKey);
37694             this.last = last; // reset the last
37695             view.focusRow(rowIndex);
37696         }else{
37697             var isSelected = this.isSelected(rowIndex);
37698             if(e.button !== 0 && isSelected){
37699                 view.focusRow(rowIndex);
37700             }else if(e.ctrlKey && isSelected){
37701                 this.deselectRow(rowIndex);
37702             }else if(!isSelected){
37703                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37704                 view.focusRow(rowIndex);
37705             }
37706         }
37707         this.fireEvent("afterselectionchange", this);
37708     },
37709     // private
37710     handleDragableRowClick :  function(grid, rowIndex, e) 
37711     {
37712         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37713             this.selectRow(rowIndex, false);
37714             var view = this.grid.view ? this.grid.view : this.grid;
37715             view.focusRow(rowIndex);
37716              this.fireEvent("afterselectionchange", this);
37717         }
37718     },
37719     
37720     /**
37721      * Selects multiple rows.
37722      * @param {Array} rows Array of the indexes of the row to select
37723      * @param {Boolean} keepExisting (optional) True to keep existing selections
37724      */
37725     selectRows : function(rows, keepExisting){
37726         if(!keepExisting){
37727             this.clearSelections();
37728         }
37729         for(var i = 0, len = rows.length; i < len; i++){
37730             this.selectRow(rows[i], true);
37731         }
37732     },
37733
37734     /**
37735      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37736      * @param {Number} startRow The index of the first row in the range
37737      * @param {Number} endRow The index of the last row in the range
37738      * @param {Boolean} keepExisting (optional) True to retain existing selections
37739      */
37740     selectRange : function(startRow, endRow, keepExisting){
37741         if(this.locked) {
37742             return;
37743         }
37744         if(!keepExisting){
37745             this.clearSelections();
37746         }
37747         if(startRow <= endRow){
37748             for(var i = startRow; i <= endRow; i++){
37749                 this.selectRow(i, true);
37750             }
37751         }else{
37752             for(var i = startRow; i >= endRow; i--){
37753                 this.selectRow(i, true);
37754             }
37755         }
37756     },
37757
37758     /**
37759      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37760      * @param {Number} startRow The index of the first row in the range
37761      * @param {Number} endRow The index of the last row in the range
37762      */
37763     deselectRange : function(startRow, endRow, preventViewNotify){
37764         if(this.locked) {
37765             return;
37766         }
37767         for(var i = startRow; i <= endRow; i++){
37768             this.deselectRow(i, preventViewNotify);
37769         }
37770     },
37771
37772     /**
37773      * Selects a row.
37774      * @param {Number} row The index of the row to select
37775      * @param {Boolean} keepExisting (optional) True to keep existing selections
37776      */
37777     selectRow : function(index, keepExisting, preventViewNotify){
37778         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
37779             return;
37780         }
37781         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37782             if(!keepExisting || this.singleSelect){
37783                 this.clearSelections();
37784             }
37785             var r = this.grid.ds.getAt(index);
37786             this.selections.add(r);
37787             this.last = this.lastActive = index;
37788             if(!preventViewNotify){
37789                 var view = this.grid.view ? this.grid.view : this.grid;
37790                 view.onRowSelect(index);
37791             }
37792             this.fireEvent("rowselect", this, index, r);
37793             this.fireEvent("selectionchange", this);
37794         }
37795     },
37796
37797     /**
37798      * Deselects a row.
37799      * @param {Number} row The index of the row to deselect
37800      */
37801     deselectRow : function(index, preventViewNotify){
37802         if(this.locked) {
37803             return;
37804         }
37805         if(this.last == index){
37806             this.last = false;
37807         }
37808         if(this.lastActive == index){
37809             this.lastActive = false;
37810         }
37811         var r = this.grid.ds.getAt(index);
37812         this.selections.remove(r);
37813         if(!preventViewNotify){
37814             var view = this.grid.view ? this.grid.view : this.grid;
37815             view.onRowDeselect(index);
37816         }
37817         this.fireEvent("rowdeselect", this, index);
37818         this.fireEvent("selectionchange", this);
37819     },
37820
37821     // private
37822     restoreLast : function(){
37823         if(this._last){
37824             this.last = this._last;
37825         }
37826     },
37827
37828     // private
37829     acceptsNav : function(row, col, cm){
37830         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37831     },
37832
37833     // private
37834     onEditorKey : function(field, e){
37835         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37836         if(k == e.TAB){
37837             e.stopEvent();
37838             ed.completeEdit();
37839             if(e.shiftKey){
37840                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37841             }else{
37842                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37843             }
37844         }else if(k == e.ENTER && !e.ctrlKey){
37845             e.stopEvent();
37846             ed.completeEdit();
37847             if(e.shiftKey){
37848                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37849             }else{
37850                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37851             }
37852         }else if(k == e.ESC){
37853             ed.cancelEdit();
37854         }
37855         if(newCell){
37856             g.startEditing(newCell[0], newCell[1]);
37857         }
37858     }
37859 });/*
37860  * Based on:
37861  * Ext JS Library 1.1.1
37862  * Copyright(c) 2006-2007, Ext JS, LLC.
37863  *
37864  * Originally Released Under LGPL - original licence link has changed is not relivant.
37865  *
37866  * Fork - LGPL
37867  * <script type="text/javascript">
37868  */
37869 /**
37870  * @class Roo.grid.CellSelectionModel
37871  * @extends Roo.grid.AbstractSelectionModel
37872  * This class provides the basic implementation for cell selection in a grid.
37873  * @constructor
37874  * @param {Object} config The object containing the configuration of this model.
37875  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37876  */
37877 Roo.grid.CellSelectionModel = function(config){
37878     Roo.apply(this, config);
37879
37880     this.selection = null;
37881
37882     this.addEvents({
37883         /**
37884              * @event beforerowselect
37885              * Fires before a cell is selected.
37886              * @param {SelectionModel} this
37887              * @param {Number} rowIndex The selected row index
37888              * @param {Number} colIndex The selected cell index
37889              */
37890             "beforecellselect" : true,
37891         /**
37892              * @event cellselect
37893              * Fires when a cell is selected.
37894              * @param {SelectionModel} this
37895              * @param {Number} rowIndex The selected row index
37896              * @param {Number} colIndex The selected cell index
37897              */
37898             "cellselect" : true,
37899         /**
37900              * @event selectionchange
37901              * Fires when the active selection changes.
37902              * @param {SelectionModel} this
37903              * @param {Object} selection null for no selection or an object (o) with two properties
37904                 <ul>
37905                 <li>o.record: the record object for the row the selection is in</li>
37906                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37907                 </ul>
37908              */
37909             "selectionchange" : true,
37910         /**
37911              * @event tabend
37912              * Fires when the tab (or enter) was pressed on the last editable cell
37913              * You can use this to trigger add new row.
37914              * @param {SelectionModel} this
37915              */
37916             "tabend" : true,
37917          /**
37918              * @event beforeeditnext
37919              * Fires before the next editable sell is made active
37920              * You can use this to skip to another cell or fire the tabend
37921              *    if you set cell to false
37922              * @param {Object} eventdata object : { cell : [ row, col ] } 
37923              */
37924             "beforeeditnext" : true
37925     });
37926     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37927 };
37928
37929 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37930     
37931     enter_is_tab: false,
37932
37933     /** @ignore */
37934     initEvents : function(){
37935         this.grid.on("mousedown", this.handleMouseDown, this);
37936         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37937         var view = this.grid.view;
37938         view.on("refresh", this.onViewChange, this);
37939         view.on("rowupdated", this.onRowUpdated, this);
37940         view.on("beforerowremoved", this.clearSelections, this);
37941         view.on("beforerowsinserted", this.clearSelections, this);
37942         if(this.grid.isEditor){
37943             this.grid.on("beforeedit", this.beforeEdit,  this);
37944         }
37945     },
37946
37947         //private
37948     beforeEdit : function(e){
37949         this.select(e.row, e.column, false, true, e.record);
37950     },
37951
37952         //private
37953     onRowUpdated : function(v, index, r){
37954         if(this.selection && this.selection.record == r){
37955             v.onCellSelect(index, this.selection.cell[1]);
37956         }
37957     },
37958
37959         //private
37960     onViewChange : function(){
37961         this.clearSelections(true);
37962     },
37963
37964         /**
37965          * Returns the currently selected cell,.
37966          * @return {Array} The selected cell (row, column) or null if none selected.
37967          */
37968     getSelectedCell : function(){
37969         return this.selection ? this.selection.cell : null;
37970     },
37971
37972     /**
37973      * Clears all selections.
37974      * @param {Boolean} true to prevent the gridview from being notified about the change.
37975      */
37976     clearSelections : function(preventNotify){
37977         var s = this.selection;
37978         if(s){
37979             if(preventNotify !== true){
37980                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37981             }
37982             this.selection = null;
37983             this.fireEvent("selectionchange", this, null);
37984         }
37985     },
37986
37987     /**
37988      * Returns true if there is a selection.
37989      * @return {Boolean}
37990      */
37991     hasSelection : function(){
37992         return this.selection ? true : false;
37993     },
37994
37995     /** @ignore */
37996     handleMouseDown : function(e, t){
37997         var v = this.grid.getView();
37998         if(this.isLocked()){
37999             return;
38000         };
38001         var row = v.findRowIndex(t);
38002         var cell = v.findCellIndex(t);
38003         if(row !== false && cell !== false){
38004             this.select(row, cell);
38005         }
38006     },
38007
38008     /**
38009      * Selects a cell.
38010      * @param {Number} rowIndex
38011      * @param {Number} collIndex
38012      */
38013     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38014         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38015             this.clearSelections();
38016             r = r || this.grid.dataSource.getAt(rowIndex);
38017             this.selection = {
38018                 record : r,
38019                 cell : [rowIndex, colIndex]
38020             };
38021             if(!preventViewNotify){
38022                 var v = this.grid.getView();
38023                 v.onCellSelect(rowIndex, colIndex);
38024                 if(preventFocus !== true){
38025                     v.focusCell(rowIndex, colIndex);
38026                 }
38027             }
38028             this.fireEvent("cellselect", this, rowIndex, colIndex);
38029             this.fireEvent("selectionchange", this, this.selection);
38030         }
38031     },
38032
38033         //private
38034     isSelectable : function(rowIndex, colIndex, cm){
38035         return !cm.isHidden(colIndex);
38036     },
38037
38038     /** @ignore */
38039     handleKeyDown : function(e){
38040         //Roo.log('Cell Sel Model handleKeyDown');
38041         if(!e.isNavKeyPress()){
38042             return;
38043         }
38044         var g = this.grid, s = this.selection;
38045         if(!s){
38046             e.stopEvent();
38047             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38048             if(cell){
38049                 this.select(cell[0], cell[1]);
38050             }
38051             return;
38052         }
38053         var sm = this;
38054         var walk = function(row, col, step){
38055             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38056         };
38057         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38058         var newCell;
38059
38060       
38061
38062         switch(k){
38063             case e.TAB:
38064                 // handled by onEditorKey
38065                 if (g.isEditor && g.editing) {
38066                     return;
38067                 }
38068                 if(e.shiftKey) {
38069                     newCell = walk(r, c-1, -1);
38070                 } else {
38071                     newCell = walk(r, c+1, 1);
38072                 }
38073                 break;
38074             
38075             case e.DOWN:
38076                newCell = walk(r+1, c, 1);
38077                 break;
38078             
38079             case e.UP:
38080                 newCell = walk(r-1, c, -1);
38081                 break;
38082             
38083             case e.RIGHT:
38084                 newCell = walk(r, c+1, 1);
38085                 break;
38086             
38087             case e.LEFT:
38088                 newCell = walk(r, c-1, -1);
38089                 break;
38090             
38091             case e.ENTER:
38092                 
38093                 if(g.isEditor && !g.editing){
38094                    g.startEditing(r, c);
38095                    e.stopEvent();
38096                    return;
38097                 }
38098                 
38099                 
38100              break;
38101         };
38102         if(newCell){
38103             this.select(newCell[0], newCell[1]);
38104             e.stopEvent();
38105             
38106         }
38107     },
38108
38109     acceptsNav : function(row, col, cm){
38110         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38111     },
38112     /**
38113      * Selects a cell.
38114      * @param {Number} field (not used) - as it's normally used as a listener
38115      * @param {Number} e - event - fake it by using
38116      *
38117      * var e = Roo.EventObjectImpl.prototype;
38118      * e.keyCode = e.TAB
38119      *
38120      * 
38121      */
38122     onEditorKey : function(field, e){
38123         
38124         var k = e.getKey(),
38125             newCell,
38126             g = this.grid,
38127             ed = g.activeEditor,
38128             forward = false;
38129         ///Roo.log('onEditorKey' + k);
38130         
38131         
38132         if (this.enter_is_tab && k == e.ENTER) {
38133             k = e.TAB;
38134         }
38135         
38136         if(k == e.TAB){
38137             if(e.shiftKey){
38138                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38139             }else{
38140                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38141                 forward = true;
38142             }
38143             
38144             e.stopEvent();
38145             
38146         } else if(k == e.ENTER &&  !e.ctrlKey){
38147             ed.completeEdit();
38148             e.stopEvent();
38149             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38150         
38151                 } else if(k == e.ESC){
38152             ed.cancelEdit();
38153         }
38154                 
38155         if (newCell) {
38156             var ecall = { cell : newCell, forward : forward };
38157             this.fireEvent('beforeeditnext', ecall );
38158             newCell = ecall.cell;
38159                         forward = ecall.forward;
38160         }
38161                 
38162         if(newCell){
38163             //Roo.log('next cell after edit');
38164             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38165         } else if (forward) {
38166             // tabbed past last
38167             this.fireEvent.defer(100, this, ['tabend',this]);
38168         }
38169     }
38170 });/*
38171  * Based on:
38172  * Ext JS Library 1.1.1
38173  * Copyright(c) 2006-2007, Ext JS, LLC.
38174  *
38175  * Originally Released Under LGPL - original licence link has changed is not relivant.
38176  *
38177  * Fork - LGPL
38178  * <script type="text/javascript">
38179  */
38180  
38181 /**
38182  * @class Roo.grid.EditorGrid
38183  * @extends Roo.grid.Grid
38184  * Class for creating and editable grid.
38185  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38186  * The container MUST have some type of size defined for the grid to fill. The container will be 
38187  * automatically set to position relative if it isn't already.
38188  * @param {Object} dataSource The data model to bind to
38189  * @param {Object} colModel The column model with info about this grid's columns
38190  */
38191 Roo.grid.EditorGrid = function(container, config){
38192     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38193     this.getGridEl().addClass("xedit-grid");
38194
38195     if(!this.selModel){
38196         this.selModel = new Roo.grid.CellSelectionModel();
38197     }
38198
38199     this.activeEditor = null;
38200
38201         this.addEvents({
38202             /**
38203              * @event beforeedit
38204              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38205              * <ul style="padding:5px;padding-left:16px;">
38206              * <li>grid - This grid</li>
38207              * <li>record - The record being edited</li>
38208              * <li>field - The field name being edited</li>
38209              * <li>value - The value for the field being edited.</li>
38210              * <li>row - The grid row index</li>
38211              * <li>column - The grid column index</li>
38212              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38213              * </ul>
38214              * @param {Object} e An edit event (see above for description)
38215              */
38216             "beforeedit" : true,
38217             /**
38218              * @event afteredit
38219              * Fires after a cell is edited. <br />
38220              * <ul style="padding:5px;padding-left:16px;">
38221              * <li>grid - This grid</li>
38222              * <li>record - The record being edited</li>
38223              * <li>field - The field name being edited</li>
38224              * <li>value - The value being set</li>
38225              * <li>originalValue - The original value for the field, before the edit.</li>
38226              * <li>row - The grid row index</li>
38227              * <li>column - The grid column index</li>
38228              * </ul>
38229              * @param {Object} e An edit event (see above for description)
38230              */
38231             "afteredit" : true,
38232             /**
38233              * @event validateedit
38234              * Fires after a cell is edited, but before the value is set in the record. 
38235          * You can use this to modify the value being set in the field, Return false
38236              * to cancel the change. The edit event object has the following properties <br />
38237              * <ul style="padding:5px;padding-left:16px;">
38238          * <li>editor - This editor</li>
38239              * <li>grid - This grid</li>
38240              * <li>record - The record being edited</li>
38241              * <li>field - The field name being edited</li>
38242              * <li>value - The value being set</li>
38243              * <li>originalValue - The original value for the field, before the edit.</li>
38244              * <li>row - The grid row index</li>
38245              * <li>column - The grid column index</li>
38246              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38247              * </ul>
38248              * @param {Object} e An edit event (see above for description)
38249              */
38250             "validateedit" : true
38251         });
38252     this.on("bodyscroll", this.stopEditing,  this);
38253     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38254 };
38255
38256 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38257     /**
38258      * @cfg {Number} clicksToEdit
38259      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38260      */
38261     clicksToEdit: 2,
38262
38263     // private
38264     isEditor : true,
38265     // private
38266     trackMouseOver: false, // causes very odd FF errors
38267
38268     onCellDblClick : function(g, row, col){
38269         this.startEditing(row, col);
38270     },
38271
38272     onEditComplete : function(ed, value, startValue){
38273         this.editing = false;
38274         this.activeEditor = null;
38275         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38276         var r = ed.record;
38277         var field = this.colModel.getDataIndex(ed.col);
38278         var e = {
38279             grid: this,
38280             record: r,
38281             field: field,
38282             originalValue: startValue,
38283             value: value,
38284             row: ed.row,
38285             column: ed.col,
38286             cancel:false,
38287             editor: ed
38288         };
38289         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
38290         cell.show();
38291           
38292         if(String(value) !== String(startValue)){
38293             
38294             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38295                 r.set(field, e.value);
38296                 // if we are dealing with a combo box..
38297                 // then we also set the 'name' colum to be the displayField
38298                 if (ed.field.displayField && ed.field.name) {
38299                     r.set(ed.field.name, ed.field.el.dom.value);
38300                 }
38301                 
38302                 delete e.cancel; //?? why!!!
38303                 this.fireEvent("afteredit", e);
38304             }
38305         } else {
38306             this.fireEvent("afteredit", e); // always fire it!
38307         }
38308         this.view.focusCell(ed.row, ed.col);
38309     },
38310
38311     /**
38312      * Starts editing the specified for the specified row/column
38313      * @param {Number} rowIndex
38314      * @param {Number} colIndex
38315      */
38316     startEditing : function(row, col){
38317         this.stopEditing();
38318         if(this.colModel.isCellEditable(col, row)){
38319             this.view.ensureVisible(row, col, true);
38320           
38321             var r = this.dataSource.getAt(row);
38322             var field = this.colModel.getDataIndex(col);
38323             var cell = Roo.get(this.view.getCell(row,col));
38324             var e = {
38325                 grid: this,
38326                 record: r,
38327                 field: field,
38328                 value: r.data[field],
38329                 row: row,
38330                 column: col,
38331                 cancel:false 
38332             };
38333             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38334                 this.editing = true;
38335                 var ed = this.colModel.getCellEditor(col, row);
38336                 
38337                 if (!ed) {
38338                     return;
38339                 }
38340                 if(!ed.rendered){
38341                     ed.render(ed.parentEl || document.body);
38342                 }
38343                 ed.field.reset();
38344                
38345                 cell.hide();
38346                 
38347                 (function(){ // complex but required for focus issues in safari, ie and opera
38348                     ed.row = row;
38349                     ed.col = col;
38350                     ed.record = r;
38351                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38352                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38353                     this.activeEditor = ed;
38354                     var v = r.data[field];
38355                     ed.startEdit(this.view.getCell(row, col), v);
38356                     // combo's with 'displayField and name set
38357                     if (ed.field.displayField && ed.field.name) {
38358                         ed.field.el.dom.value = r.data[ed.field.name];
38359                     }
38360                     
38361                     
38362                 }).defer(50, this);
38363             }
38364         }
38365     },
38366         
38367     /**
38368      * Stops any active editing
38369      */
38370     stopEditing : function(){
38371         if(this.activeEditor){
38372             this.activeEditor.completeEdit();
38373         }
38374         this.activeEditor = null;
38375     },
38376         
38377          /**
38378      * Called to get grid's drag proxy text, by default returns this.ddText.
38379      * @return {String}
38380      */
38381     getDragDropText : function(){
38382         var count = this.selModel.getSelectedCell() ? 1 : 0;
38383         return String.format(this.ddText, count, count == 1 ? '' : 's');
38384     }
38385         
38386 });/*
38387  * Based on:
38388  * Ext JS Library 1.1.1
38389  * Copyright(c) 2006-2007, Ext JS, LLC.
38390  *
38391  * Originally Released Under LGPL - original licence link has changed is not relivant.
38392  *
38393  * Fork - LGPL
38394  * <script type="text/javascript">
38395  */
38396
38397 // private - not really -- you end up using it !
38398 // This is a support class used internally by the Grid components
38399
38400 /**
38401  * @class Roo.grid.GridEditor
38402  * @extends Roo.Editor
38403  * Class for creating and editable grid elements.
38404  * @param {Object} config any settings (must include field)
38405  */
38406 Roo.grid.GridEditor = function(field, config){
38407     if (!config && field.field) {
38408         config = field;
38409         field = Roo.factory(config.field, Roo.form);
38410     }
38411     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38412     field.monitorTab = false;
38413 };
38414
38415 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38416     
38417     /**
38418      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38419      */
38420     
38421     alignment: "tl-tl",
38422     autoSize: "width",
38423     hideEl : false,
38424     cls: "x-small-editor x-grid-editor",
38425     shim:false,
38426     shadow:"frame"
38427 });/*
38428  * Based on:
38429  * Ext JS Library 1.1.1
38430  * Copyright(c) 2006-2007, Ext JS, LLC.
38431  *
38432  * Originally Released Under LGPL - original licence link has changed is not relivant.
38433  *
38434  * Fork - LGPL
38435  * <script type="text/javascript">
38436  */
38437   
38438
38439   
38440 Roo.grid.PropertyRecord = Roo.data.Record.create([
38441     {name:'name',type:'string'},  'value'
38442 ]);
38443
38444
38445 Roo.grid.PropertyStore = function(grid, source){
38446     this.grid = grid;
38447     this.store = new Roo.data.Store({
38448         recordType : Roo.grid.PropertyRecord
38449     });
38450     this.store.on('update', this.onUpdate,  this);
38451     if(source){
38452         this.setSource(source);
38453     }
38454     Roo.grid.PropertyStore.superclass.constructor.call(this);
38455 };
38456
38457
38458
38459 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38460     setSource : function(o){
38461         this.source = o;
38462         this.store.removeAll();
38463         var data = [];
38464         for(var k in o){
38465             if(this.isEditableValue(o[k])){
38466                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38467             }
38468         }
38469         this.store.loadRecords({records: data}, {}, true);
38470     },
38471
38472     onUpdate : function(ds, record, type){
38473         if(type == Roo.data.Record.EDIT){
38474             var v = record.data['value'];
38475             var oldValue = record.modified['value'];
38476             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38477                 this.source[record.id] = v;
38478                 record.commit();
38479                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38480             }else{
38481                 record.reject();
38482             }
38483         }
38484     },
38485
38486     getProperty : function(row){
38487        return this.store.getAt(row);
38488     },
38489
38490     isEditableValue: function(val){
38491         if(val && val instanceof Date){
38492             return true;
38493         }else if(typeof val == 'object' || typeof val == 'function'){
38494             return false;
38495         }
38496         return true;
38497     },
38498
38499     setValue : function(prop, value){
38500         this.source[prop] = value;
38501         this.store.getById(prop).set('value', value);
38502     },
38503
38504     getSource : function(){
38505         return this.source;
38506     }
38507 });
38508
38509 Roo.grid.PropertyColumnModel = function(grid, store){
38510     this.grid = grid;
38511     var g = Roo.grid;
38512     g.PropertyColumnModel.superclass.constructor.call(this, [
38513         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38514         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38515     ]);
38516     this.store = store;
38517     this.bselect = Roo.DomHelper.append(document.body, {
38518         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38519             {tag: 'option', value: 'true', html: 'true'},
38520             {tag: 'option', value: 'false', html: 'false'}
38521         ]
38522     });
38523     Roo.id(this.bselect);
38524     var f = Roo.form;
38525     this.editors = {
38526         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38527         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38528         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38529         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38530         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38531     };
38532     this.renderCellDelegate = this.renderCell.createDelegate(this);
38533     this.renderPropDelegate = this.renderProp.createDelegate(this);
38534 };
38535
38536 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38537     
38538     
38539     nameText : 'Name',
38540     valueText : 'Value',
38541     
38542     dateFormat : 'm/j/Y',
38543     
38544     
38545     renderDate : function(dateVal){
38546         return dateVal.dateFormat(this.dateFormat);
38547     },
38548
38549     renderBool : function(bVal){
38550         return bVal ? 'true' : 'false';
38551     },
38552
38553     isCellEditable : function(colIndex, rowIndex){
38554         return colIndex == 1;
38555     },
38556
38557     getRenderer : function(col){
38558         return col == 1 ?
38559             this.renderCellDelegate : this.renderPropDelegate;
38560     },
38561
38562     renderProp : function(v){
38563         return this.getPropertyName(v);
38564     },
38565
38566     renderCell : function(val){
38567         var rv = val;
38568         if(val instanceof Date){
38569             rv = this.renderDate(val);
38570         }else if(typeof val == 'boolean'){
38571             rv = this.renderBool(val);
38572         }
38573         return Roo.util.Format.htmlEncode(rv);
38574     },
38575
38576     getPropertyName : function(name){
38577         var pn = this.grid.propertyNames;
38578         return pn && pn[name] ? pn[name] : name;
38579     },
38580
38581     getCellEditor : function(colIndex, rowIndex){
38582         var p = this.store.getProperty(rowIndex);
38583         var n = p.data['name'], val = p.data['value'];
38584         
38585         if(typeof(this.grid.customEditors[n]) == 'string'){
38586             return this.editors[this.grid.customEditors[n]];
38587         }
38588         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38589             return this.grid.customEditors[n];
38590         }
38591         if(val instanceof Date){
38592             return this.editors['date'];
38593         }else if(typeof val == 'number'){
38594             return this.editors['number'];
38595         }else if(typeof val == 'boolean'){
38596             return this.editors['boolean'];
38597         }else{
38598             return this.editors['string'];
38599         }
38600     }
38601 });
38602
38603 /**
38604  * @class Roo.grid.PropertyGrid
38605  * @extends Roo.grid.EditorGrid
38606  * This class represents the  interface of a component based property grid control.
38607  * <br><br>Usage:<pre><code>
38608  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38609       
38610  });
38611  // set any options
38612  grid.render();
38613  * </code></pre>
38614   
38615  * @constructor
38616  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38617  * The container MUST have some type of size defined for the grid to fill. The container will be
38618  * automatically set to position relative if it isn't already.
38619  * @param {Object} config A config object that sets properties on this grid.
38620  */
38621 Roo.grid.PropertyGrid = function(container, config){
38622     config = config || {};
38623     var store = new Roo.grid.PropertyStore(this);
38624     this.store = store;
38625     var cm = new Roo.grid.PropertyColumnModel(this, store);
38626     store.store.sort('name', 'ASC');
38627     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38628         ds: store.store,
38629         cm: cm,
38630         enableColLock:false,
38631         enableColumnMove:false,
38632         stripeRows:false,
38633         trackMouseOver: false,
38634         clicksToEdit:1
38635     }, config));
38636     this.getGridEl().addClass('x-props-grid');
38637     this.lastEditRow = null;
38638     this.on('columnresize', this.onColumnResize, this);
38639     this.addEvents({
38640          /**
38641              * @event beforepropertychange
38642              * Fires before a property changes (return false to stop?)
38643              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38644              * @param {String} id Record Id
38645              * @param {String} newval New Value
38646          * @param {String} oldval Old Value
38647              */
38648         "beforepropertychange": true,
38649         /**
38650              * @event propertychange
38651              * Fires after a property changes
38652              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38653              * @param {String} id Record Id
38654              * @param {String} newval New Value
38655          * @param {String} oldval Old Value
38656              */
38657         "propertychange": true
38658     });
38659     this.customEditors = this.customEditors || {};
38660 };
38661 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38662     
38663      /**
38664      * @cfg {Object} customEditors map of colnames=> custom editors.
38665      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38666      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38667      * false disables editing of the field.
38668          */
38669     
38670       /**
38671      * @cfg {Object} propertyNames map of property Names to their displayed value
38672          */
38673     
38674     render : function(){
38675         Roo.grid.PropertyGrid.superclass.render.call(this);
38676         this.autoSize.defer(100, this);
38677     },
38678
38679     autoSize : function(){
38680         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38681         if(this.view){
38682             this.view.fitColumns();
38683         }
38684     },
38685
38686     onColumnResize : function(){
38687         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38688         this.autoSize();
38689     },
38690     /**
38691      * Sets the data for the Grid
38692      * accepts a Key => Value object of all the elements avaiable.
38693      * @param {Object} data  to appear in grid.
38694      */
38695     setSource : function(source){
38696         this.store.setSource(source);
38697         //this.autoSize();
38698     },
38699     /**
38700      * Gets all the data from the grid.
38701      * @return {Object} data  data stored in grid
38702      */
38703     getSource : function(){
38704         return this.store.getSource();
38705     }
38706 });/*
38707   
38708  * Licence LGPL
38709  
38710  */
38711  
38712 /**
38713  * @class Roo.grid.Calendar
38714  * @extends Roo.grid.Grid
38715  * This class extends the Grid to provide a calendar widget
38716  * <br><br>Usage:<pre><code>
38717  var grid = new Roo.grid.Calendar("my-container-id", {
38718      ds: myDataStore,
38719      cm: myColModel,
38720      selModel: mySelectionModel,
38721      autoSizeColumns: true,
38722      monitorWindowResize: false,
38723      trackMouseOver: true
38724      eventstore : real data store..
38725  });
38726  // set any options
38727  grid.render();
38728   
38729   * @constructor
38730  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38731  * The container MUST have some type of size defined for the grid to fill. The container will be
38732  * automatically set to position relative if it isn't already.
38733  * @param {Object} config A config object that sets properties on this grid.
38734  */
38735 Roo.grid.Calendar = function(container, config){
38736         // initialize the container
38737         this.container = Roo.get(container);
38738         this.container.update("");
38739         this.container.setStyle("overflow", "hidden");
38740     this.container.addClass('x-grid-container');
38741
38742     this.id = this.container.id;
38743
38744     Roo.apply(this, config);
38745     // check and correct shorthanded configs
38746     
38747     var rows = [];
38748     var d =1;
38749     for (var r = 0;r < 6;r++) {
38750         
38751         rows[r]=[];
38752         for (var c =0;c < 7;c++) {
38753             rows[r][c]= '';
38754         }
38755     }
38756     if (this.eventStore) {
38757         this.eventStore= Roo.factory(this.eventStore, Roo.data);
38758         this.eventStore.on('load',this.onLoad, this);
38759         this.eventStore.on('beforeload',this.clearEvents, this);
38760          
38761     }
38762     
38763     this.dataSource = new Roo.data.Store({
38764             proxy: new Roo.data.MemoryProxy(rows),
38765             reader: new Roo.data.ArrayReader({}, [
38766                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
38767     });
38768
38769     this.dataSource.load();
38770     this.ds = this.dataSource;
38771     this.ds.xmodule = this.xmodule || false;
38772     
38773     
38774     var cellRender = function(v,x,r)
38775     {
38776         return String.format(
38777             '<div class="fc-day  fc-widget-content"><div>' +
38778                 '<div class="fc-event-container"></div>' +
38779                 '<div class="fc-day-number">{0}</div>'+
38780                 
38781                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
38782             '</div></div>', v);
38783     
38784     }
38785     
38786     
38787     this.colModel = new Roo.grid.ColumnModel( [
38788         {
38789             xtype: 'ColumnModel',
38790             xns: Roo.grid,
38791             dataIndex : 'weekday0',
38792             header : 'Sunday',
38793             renderer : cellRender
38794         },
38795         {
38796             xtype: 'ColumnModel',
38797             xns: Roo.grid,
38798             dataIndex : 'weekday1',
38799             header : 'Monday',
38800             renderer : cellRender
38801         },
38802         {
38803             xtype: 'ColumnModel',
38804             xns: Roo.grid,
38805             dataIndex : 'weekday2',
38806             header : 'Tuesday',
38807             renderer : cellRender
38808         },
38809         {
38810             xtype: 'ColumnModel',
38811             xns: Roo.grid,
38812             dataIndex : 'weekday3',
38813             header : 'Wednesday',
38814             renderer : cellRender
38815         },
38816         {
38817             xtype: 'ColumnModel',
38818             xns: Roo.grid,
38819             dataIndex : 'weekday4',
38820             header : 'Thursday',
38821             renderer : cellRender
38822         },
38823         {
38824             xtype: 'ColumnModel',
38825             xns: Roo.grid,
38826             dataIndex : 'weekday5',
38827             header : 'Friday',
38828             renderer : cellRender
38829         },
38830         {
38831             xtype: 'ColumnModel',
38832             xns: Roo.grid,
38833             dataIndex : 'weekday6',
38834             header : 'Saturday',
38835             renderer : cellRender
38836         }
38837     ]);
38838     this.cm = this.colModel;
38839     this.cm.xmodule = this.xmodule || false;
38840  
38841         
38842           
38843     //this.selModel = new Roo.grid.CellSelectionModel();
38844     //this.sm = this.selModel;
38845     //this.selModel.init(this);
38846     
38847     
38848     if(this.width){
38849         this.container.setWidth(this.width);
38850     }
38851
38852     if(this.height){
38853         this.container.setHeight(this.height);
38854     }
38855     /** @private */
38856         this.addEvents({
38857         // raw events
38858         /**
38859          * @event click
38860          * The raw click event for the entire grid.
38861          * @param {Roo.EventObject} e
38862          */
38863         "click" : true,
38864         /**
38865          * @event dblclick
38866          * The raw dblclick event for the entire grid.
38867          * @param {Roo.EventObject} e
38868          */
38869         "dblclick" : true,
38870         /**
38871          * @event contextmenu
38872          * The raw contextmenu event for the entire grid.
38873          * @param {Roo.EventObject} e
38874          */
38875         "contextmenu" : true,
38876         /**
38877          * @event mousedown
38878          * The raw mousedown event for the entire grid.
38879          * @param {Roo.EventObject} e
38880          */
38881         "mousedown" : true,
38882         /**
38883          * @event mouseup
38884          * The raw mouseup event for the entire grid.
38885          * @param {Roo.EventObject} e
38886          */
38887         "mouseup" : true,
38888         /**
38889          * @event mouseover
38890          * The raw mouseover event for the entire grid.
38891          * @param {Roo.EventObject} e
38892          */
38893         "mouseover" : true,
38894         /**
38895          * @event mouseout
38896          * The raw mouseout event for the entire grid.
38897          * @param {Roo.EventObject} e
38898          */
38899         "mouseout" : true,
38900         /**
38901          * @event keypress
38902          * The raw keypress event for the entire grid.
38903          * @param {Roo.EventObject} e
38904          */
38905         "keypress" : true,
38906         /**
38907          * @event keydown
38908          * The raw keydown event for the entire grid.
38909          * @param {Roo.EventObject} e
38910          */
38911         "keydown" : true,
38912
38913         // custom events
38914
38915         /**
38916          * @event cellclick
38917          * Fires when a cell is clicked
38918          * @param {Grid} this
38919          * @param {Number} rowIndex
38920          * @param {Number} columnIndex
38921          * @param {Roo.EventObject} e
38922          */
38923         "cellclick" : true,
38924         /**
38925          * @event celldblclick
38926          * Fires when a cell is double clicked
38927          * @param {Grid} this
38928          * @param {Number} rowIndex
38929          * @param {Number} columnIndex
38930          * @param {Roo.EventObject} e
38931          */
38932         "celldblclick" : true,
38933         /**
38934          * @event rowclick
38935          * Fires when a row is clicked
38936          * @param {Grid} this
38937          * @param {Number} rowIndex
38938          * @param {Roo.EventObject} e
38939          */
38940         "rowclick" : true,
38941         /**
38942          * @event rowdblclick
38943          * Fires when a row is double clicked
38944          * @param {Grid} this
38945          * @param {Number} rowIndex
38946          * @param {Roo.EventObject} e
38947          */
38948         "rowdblclick" : true,
38949         /**
38950          * @event headerclick
38951          * Fires when a header is clicked
38952          * @param {Grid} this
38953          * @param {Number} columnIndex
38954          * @param {Roo.EventObject} e
38955          */
38956         "headerclick" : true,
38957         /**
38958          * @event headerdblclick
38959          * Fires when a header cell is double clicked
38960          * @param {Grid} this
38961          * @param {Number} columnIndex
38962          * @param {Roo.EventObject} e
38963          */
38964         "headerdblclick" : true,
38965         /**
38966          * @event rowcontextmenu
38967          * Fires when a row is right clicked
38968          * @param {Grid} this
38969          * @param {Number} rowIndex
38970          * @param {Roo.EventObject} e
38971          */
38972         "rowcontextmenu" : true,
38973         /**
38974          * @event cellcontextmenu
38975          * Fires when a cell is right clicked
38976          * @param {Grid} this
38977          * @param {Number} rowIndex
38978          * @param {Number} cellIndex
38979          * @param {Roo.EventObject} e
38980          */
38981          "cellcontextmenu" : true,
38982         /**
38983          * @event headercontextmenu
38984          * Fires when a header is right clicked
38985          * @param {Grid} this
38986          * @param {Number} columnIndex
38987          * @param {Roo.EventObject} e
38988          */
38989         "headercontextmenu" : true,
38990         /**
38991          * @event bodyscroll
38992          * Fires when the body element is scrolled
38993          * @param {Number} scrollLeft
38994          * @param {Number} scrollTop
38995          */
38996         "bodyscroll" : true,
38997         /**
38998          * @event columnresize
38999          * Fires when the user resizes a column
39000          * @param {Number} columnIndex
39001          * @param {Number} newSize
39002          */
39003         "columnresize" : true,
39004         /**
39005          * @event columnmove
39006          * Fires when the user moves a column
39007          * @param {Number} oldIndex
39008          * @param {Number} newIndex
39009          */
39010         "columnmove" : true,
39011         /**
39012          * @event startdrag
39013          * Fires when row(s) start being dragged
39014          * @param {Grid} this
39015          * @param {Roo.GridDD} dd The drag drop object
39016          * @param {event} e The raw browser event
39017          */
39018         "startdrag" : true,
39019         /**
39020          * @event enddrag
39021          * Fires when a drag operation is complete
39022          * @param {Grid} this
39023          * @param {Roo.GridDD} dd The drag drop object
39024          * @param {event} e The raw browser event
39025          */
39026         "enddrag" : true,
39027         /**
39028          * @event dragdrop
39029          * Fires when dragged row(s) are dropped on a valid DD target
39030          * @param {Grid} this
39031          * @param {Roo.GridDD} dd The drag drop object
39032          * @param {String} targetId The target drag drop object
39033          * @param {event} e The raw browser event
39034          */
39035         "dragdrop" : true,
39036         /**
39037          * @event dragover
39038          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
39039          * @param {Grid} this
39040          * @param {Roo.GridDD} dd The drag drop object
39041          * @param {String} targetId The target drag drop object
39042          * @param {event} e The raw browser event
39043          */
39044         "dragover" : true,
39045         /**
39046          * @event dragenter
39047          *  Fires when the dragged row(s) first cross another DD target while being dragged
39048          * @param {Grid} this
39049          * @param {Roo.GridDD} dd The drag drop object
39050          * @param {String} targetId The target drag drop object
39051          * @param {event} e The raw browser event
39052          */
39053         "dragenter" : true,
39054         /**
39055          * @event dragout
39056          * Fires when the dragged row(s) leave another DD target while being dragged
39057          * @param {Grid} this
39058          * @param {Roo.GridDD} dd The drag drop object
39059          * @param {String} targetId The target drag drop object
39060          * @param {event} e The raw browser event
39061          */
39062         "dragout" : true,
39063         /**
39064          * @event rowclass
39065          * Fires when a row is rendered, so you can change add a style to it.
39066          * @param {GridView} gridview   The grid view
39067          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
39068          */
39069         'rowclass' : true,
39070
39071         /**
39072          * @event render
39073          * Fires when the grid is rendered
39074          * @param {Grid} grid
39075          */
39076         'render' : true,
39077             /**
39078              * @event select
39079              * Fires when a date is selected
39080              * @param {DatePicker} this
39081              * @param {Date} date The selected date
39082              */
39083         'select': true,
39084         /**
39085              * @event monthchange
39086              * Fires when the displayed month changes 
39087              * @param {DatePicker} this
39088              * @param {Date} date The selected month
39089              */
39090         'monthchange': true,
39091         /**
39092              * @event evententer
39093              * Fires when mouse over an event
39094              * @param {Calendar} this
39095              * @param {event} Event
39096              */
39097         'evententer': true,
39098         /**
39099              * @event eventleave
39100              * Fires when the mouse leaves an
39101              * @param {Calendar} this
39102              * @param {event}
39103              */
39104         'eventleave': true,
39105         /**
39106              * @event eventclick
39107              * Fires when the mouse click an
39108              * @param {Calendar} this
39109              * @param {event}
39110              */
39111         'eventclick': true,
39112         /**
39113              * @event eventrender
39114              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
39115              * @param {Calendar} this
39116              * @param {data} data to be modified
39117              */
39118         'eventrender': true
39119         
39120     });
39121
39122     Roo.grid.Grid.superclass.constructor.call(this);
39123     this.on('render', function() {
39124         this.view.el.addClass('x-grid-cal'); 
39125         
39126         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
39127
39128     },this);
39129     
39130     if (!Roo.grid.Calendar.style) {
39131         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
39132             
39133             
39134             '.x-grid-cal .x-grid-col' :  {
39135                 height: 'auto !important',
39136                 'vertical-align': 'top'
39137             },
39138             '.x-grid-cal  .fc-event-hori' : {
39139                 height: '14px'
39140             }
39141              
39142             
39143         }, Roo.id());
39144     }
39145
39146     
39147     
39148 };
39149 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
39150     /**
39151      * @cfg {Store} eventStore The store that loads events.
39152      */
39153     eventStore : 25,
39154
39155      
39156     activeDate : false,
39157     startDay : 0,
39158     autoWidth : true,
39159     monitorWindowResize : false,
39160
39161     
39162     resizeColumns : function() {
39163         var col = (this.view.el.getWidth() / 7) - 3;
39164         // loop through cols, and setWidth
39165         for(var i =0 ; i < 7 ; i++){
39166             this.cm.setColumnWidth(i, col);
39167         }
39168     },
39169      setDate :function(date) {
39170         
39171         Roo.log('setDate?');
39172         
39173         this.resizeColumns();
39174         var vd = this.activeDate;
39175         this.activeDate = date;
39176 //        if(vd && this.el){
39177 //            var t = date.getTime();
39178 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
39179 //                Roo.log('using add remove');
39180 //                
39181 //                this.fireEvent('monthchange', this, date);
39182 //                
39183 //                this.cells.removeClass("fc-state-highlight");
39184 //                this.cells.each(function(c){
39185 //                   if(c.dateValue == t){
39186 //                       c.addClass("fc-state-highlight");
39187 //                       setTimeout(function(){
39188 //                            try{c.dom.firstChild.focus();}catch(e){}
39189 //                       }, 50);
39190 //                       return false;
39191 //                   }
39192 //                   return true;
39193 //                });
39194 //                return;
39195 //            }
39196 //        }
39197         
39198         var days = date.getDaysInMonth();
39199         
39200         var firstOfMonth = date.getFirstDateOfMonth();
39201         var startingPos = firstOfMonth.getDay()-this.startDay;
39202         
39203         if(startingPos < this.startDay){
39204             startingPos += 7;
39205         }
39206         
39207         var pm = date.add(Date.MONTH, -1);
39208         var prevStart = pm.getDaysInMonth()-startingPos;
39209 //        
39210         
39211         
39212         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39213         
39214         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
39215         //this.cells.addClassOnOver('fc-state-hover');
39216         
39217         var cells = this.cells.elements;
39218         var textEls = this.textNodes;
39219         
39220         //Roo.each(cells, function(cell){
39221         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
39222         //});
39223         
39224         days += startingPos;
39225
39226         // convert everything to numbers so it's fast
39227         var day = 86400000;
39228         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
39229         //Roo.log(d);
39230         //Roo.log(pm);
39231         //Roo.log(prevStart);
39232         
39233         var today = new Date().clearTime().getTime();
39234         var sel = date.clearTime().getTime();
39235         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
39236         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
39237         var ddMatch = this.disabledDatesRE;
39238         var ddText = this.disabledDatesText;
39239         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
39240         var ddaysText = this.disabledDaysText;
39241         var format = this.format;
39242         
39243         var setCellClass = function(cal, cell){
39244             
39245             //Roo.log('set Cell Class');
39246             cell.title = "";
39247             var t = d.getTime();
39248             
39249             //Roo.log(d);
39250             
39251             
39252             cell.dateValue = t;
39253             if(t == today){
39254                 cell.className += " fc-today";
39255                 cell.className += " fc-state-highlight";
39256                 cell.title = cal.todayText;
39257             }
39258             if(t == sel){
39259                 // disable highlight in other month..
39260                 cell.className += " fc-state-highlight";
39261                 
39262             }
39263             // disabling
39264             if(t < min) {
39265                 //cell.className = " fc-state-disabled";
39266                 cell.title = cal.minText;
39267                 return;
39268             }
39269             if(t > max) {
39270                 //cell.className = " fc-state-disabled";
39271                 cell.title = cal.maxText;
39272                 return;
39273             }
39274             if(ddays){
39275                 if(ddays.indexOf(d.getDay()) != -1){
39276                     // cell.title = ddaysText;
39277                    // cell.className = " fc-state-disabled";
39278                 }
39279             }
39280             if(ddMatch && format){
39281                 var fvalue = d.dateFormat(format);
39282                 if(ddMatch.test(fvalue)){
39283                     cell.title = ddText.replace("%0", fvalue);
39284                    cell.className = " fc-state-disabled";
39285                 }
39286             }
39287             
39288             if (!cell.initialClassName) {
39289                 cell.initialClassName = cell.dom.className;
39290             }
39291             
39292             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
39293         };
39294
39295         var i = 0;
39296         
39297         for(; i < startingPos; i++) {
39298             cells[i].dayName =  (++prevStart);
39299             Roo.log(textEls[i]);
39300             d.setDate(d.getDate()+1);
39301             
39302             //cells[i].className = "fc-past fc-other-month";
39303             setCellClass(this, cells[i]);
39304         }
39305         
39306         var intDay = 0;
39307         
39308         for(; i < days; i++){
39309             intDay = i - startingPos + 1;
39310             cells[i].dayName =  (intDay);
39311             d.setDate(d.getDate()+1);
39312             
39313             cells[i].className = ''; // "x-date-active";
39314             setCellClass(this, cells[i]);
39315         }
39316         var extraDays = 0;
39317         
39318         for(; i < 42; i++) {
39319             //textEls[i].innerHTML = (++extraDays);
39320             
39321             d.setDate(d.getDate()+1);
39322             cells[i].dayName = (++extraDays);
39323             cells[i].className = "fc-future fc-other-month";
39324             setCellClass(this, cells[i]);
39325         }
39326         
39327         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
39328         
39329         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
39330         
39331         // this will cause all the cells to mis
39332         var rows= [];
39333         var i =0;
39334         for (var r = 0;r < 6;r++) {
39335             for (var c =0;c < 7;c++) {
39336                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
39337             }    
39338         }
39339         
39340         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
39341         for(i=0;i<cells.length;i++) {
39342             
39343             this.cells.elements[i].dayName = cells[i].dayName ;
39344             this.cells.elements[i].className = cells[i].className;
39345             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
39346             this.cells.elements[i].title = cells[i].title ;
39347             this.cells.elements[i].dateValue = cells[i].dateValue ;
39348         }
39349         
39350         
39351         
39352         
39353         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
39354         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
39355         
39356         ////if(totalRows != 6){
39357             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
39358            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
39359        // }
39360         
39361         this.fireEvent('monthchange', this, date);
39362         
39363         
39364     },
39365  /**
39366      * Returns the grid's SelectionModel.
39367      * @return {SelectionModel}
39368      */
39369     getSelectionModel : function(){
39370         if(!this.selModel){
39371             this.selModel = new Roo.grid.CellSelectionModel();
39372         }
39373         return this.selModel;
39374     },
39375
39376     load: function() {
39377         this.eventStore.load()
39378         
39379         
39380         
39381     },
39382     
39383     findCell : function(dt) {
39384         dt = dt.clearTime().getTime();
39385         var ret = false;
39386         this.cells.each(function(c){
39387             //Roo.log("check " +c.dateValue + '?=' + dt);
39388             if(c.dateValue == dt){
39389                 ret = c;
39390                 return false;
39391             }
39392             return true;
39393         });
39394         
39395         return ret;
39396     },
39397     
39398     findCells : function(rec) {
39399         var s = rec.data.start_dt.clone().clearTime().getTime();
39400        // Roo.log(s);
39401         var e= rec.data.end_dt.clone().clearTime().getTime();
39402        // Roo.log(e);
39403         var ret = [];
39404         this.cells.each(function(c){
39405              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
39406             
39407             if(c.dateValue > e){
39408                 return ;
39409             }
39410             if(c.dateValue < s){
39411                 return ;
39412             }
39413             ret.push(c);
39414         });
39415         
39416         return ret;    
39417     },
39418     
39419     findBestRow: function(cells)
39420     {
39421         var ret = 0;
39422         
39423         for (var i =0 ; i < cells.length;i++) {
39424             ret  = Math.max(cells[i].rows || 0,ret);
39425         }
39426         return ret;
39427         
39428     },
39429     
39430     
39431     addItem : function(rec)
39432     {
39433         // look for vertical location slot in
39434         var cells = this.findCells(rec);
39435         
39436         rec.row = this.findBestRow(cells);
39437         
39438         // work out the location.
39439         
39440         var crow = false;
39441         var rows = [];
39442         for(var i =0; i < cells.length; i++) {
39443             if (!crow) {
39444                 crow = {
39445                     start : cells[i],
39446                     end :  cells[i]
39447                 };
39448                 continue;
39449             }
39450             if (crow.start.getY() == cells[i].getY()) {
39451                 // on same row.
39452                 crow.end = cells[i];
39453                 continue;
39454             }
39455             // different row.
39456             rows.push(crow);
39457             crow = {
39458                 start: cells[i],
39459                 end : cells[i]
39460             };
39461             
39462         }
39463         
39464         rows.push(crow);
39465         rec.els = [];
39466         rec.rows = rows;
39467         rec.cells = cells;
39468         for (var i = 0; i < cells.length;i++) {
39469             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
39470             
39471         }
39472         
39473         
39474     },
39475     
39476     clearEvents: function() {
39477         
39478         if (!this.eventStore.getCount()) {
39479             return;
39480         }
39481         // reset number of rows in cells.
39482         Roo.each(this.cells.elements, function(c){
39483             c.rows = 0;
39484         });
39485         
39486         this.eventStore.each(function(e) {
39487             this.clearEvent(e);
39488         },this);
39489         
39490     },
39491     
39492     clearEvent : function(ev)
39493     {
39494         if (ev.els) {
39495             Roo.each(ev.els, function(el) {
39496                 el.un('mouseenter' ,this.onEventEnter, this);
39497                 el.un('mouseleave' ,this.onEventLeave, this);
39498                 el.remove();
39499             },this);
39500             ev.els = [];
39501         }
39502     },
39503     
39504     
39505     renderEvent : function(ev,ctr) {
39506         if (!ctr) {
39507              ctr = this.view.el.select('.fc-event-container',true).first();
39508         }
39509         
39510          
39511         this.clearEvent(ev);
39512             //code
39513        
39514         
39515         
39516         ev.els = [];
39517         var cells = ev.cells;
39518         var rows = ev.rows;
39519         this.fireEvent('eventrender', this, ev);
39520         
39521         for(var i =0; i < rows.length; i++) {
39522             
39523             cls = '';
39524             if (i == 0) {
39525                 cls += ' fc-event-start';
39526             }
39527             if ((i+1) == rows.length) {
39528                 cls += ' fc-event-end';
39529             }
39530             
39531             //Roo.log(ev.data);
39532             // how many rows should it span..
39533             var cg = this.eventTmpl.append(ctr,Roo.apply({
39534                 fccls : cls
39535                 
39536             }, ev.data) , true);
39537             
39538             
39539             cg.on('mouseenter' ,this.onEventEnter, this, ev);
39540             cg.on('mouseleave' ,this.onEventLeave, this, ev);
39541             cg.on('click', this.onEventClick, this, ev);
39542             
39543             ev.els.push(cg);
39544             
39545             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
39546             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
39547             //Roo.log(cg);
39548              
39549             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
39550             cg.setWidth(ebox.right - sbox.x -2);
39551         }
39552     },
39553     
39554     renderEvents: function()
39555     {   
39556         // first make sure there is enough space..
39557         
39558         if (!this.eventTmpl) {
39559             this.eventTmpl = new Roo.Template(
39560                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
39561                     '<div class="fc-event-inner">' +
39562                         '<span class="fc-event-time">{time}</span>' +
39563                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
39564                     '</div>' +
39565                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
39566                 '</div>'
39567             );
39568                 
39569         }
39570                
39571         
39572         
39573         this.cells.each(function(c) {
39574             //Roo.log(c.select('.fc-day-content div',true).first());
39575             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
39576         });
39577         
39578         var ctr = this.view.el.select('.fc-event-container',true).first();
39579         
39580         var cls;
39581         this.eventStore.each(function(ev){
39582             
39583             this.renderEvent(ev);
39584              
39585              
39586         }, this);
39587         this.view.layout();
39588         
39589     },
39590     
39591     onEventEnter: function (e, el,event,d) {
39592         this.fireEvent('evententer', this, el, event);
39593     },
39594     
39595     onEventLeave: function (e, el,event,d) {
39596         this.fireEvent('eventleave', this, el, event);
39597     },
39598     
39599     onEventClick: function (e, el,event,d) {
39600         this.fireEvent('eventclick', this, el, event);
39601     },
39602     
39603     onMonthChange: function () {
39604         this.store.load();
39605     },
39606     
39607     onLoad: function () {
39608         
39609         //Roo.log('calendar onload');
39610 //         
39611         if(this.eventStore.getCount() > 0){
39612             
39613            
39614             
39615             this.eventStore.each(function(d){
39616                 
39617                 
39618                 // FIXME..
39619                 var add =   d.data;
39620                 if (typeof(add.end_dt) == 'undefined')  {
39621                     Roo.log("Missing End time in calendar data: ");
39622                     Roo.log(d);
39623                     return;
39624                 }
39625                 if (typeof(add.start_dt) == 'undefined')  {
39626                     Roo.log("Missing Start time in calendar data: ");
39627                     Roo.log(d);
39628                     return;
39629                 }
39630                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
39631                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
39632                 add.id = add.id || d.id;
39633                 add.title = add.title || '??';
39634                 
39635                 this.addItem(d);
39636                 
39637              
39638             },this);
39639         }
39640         
39641         this.renderEvents();
39642     }
39643     
39644
39645 });
39646 /*
39647  grid : {
39648                 xtype: 'Grid',
39649                 xns: Roo.grid,
39650                 listeners : {
39651                     render : function ()
39652                     {
39653                         _this.grid = this;
39654                         
39655                         if (!this.view.el.hasClass('course-timesheet')) {
39656                             this.view.el.addClass('course-timesheet');
39657                         }
39658                         if (this.tsStyle) {
39659                             this.ds.load({});
39660                             return; 
39661                         }
39662                         Roo.log('width');
39663                         Roo.log(_this.grid.view.el.getWidth());
39664                         
39665                         
39666                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
39667                             '.course-timesheet .x-grid-row' : {
39668                                 height: '80px'
39669                             },
39670                             '.x-grid-row td' : {
39671                                 'vertical-align' : 0
39672                             },
39673                             '.course-edit-link' : {
39674                                 'color' : 'blue',
39675                                 'text-overflow' : 'ellipsis',
39676                                 'overflow' : 'hidden',
39677                                 'white-space' : 'nowrap',
39678                                 'cursor' : 'pointer'
39679                             },
39680                             '.sub-link' : {
39681                                 'color' : 'green'
39682                             },
39683                             '.de-act-sup-link' : {
39684                                 'color' : 'purple',
39685                                 'text-decoration' : 'line-through'
39686                             },
39687                             '.de-act-link' : {
39688                                 'color' : 'red',
39689                                 'text-decoration' : 'line-through'
39690                             },
39691                             '.course-timesheet .course-highlight' : {
39692                                 'border-top-style': 'dashed !important',
39693                                 'border-bottom-bottom': 'dashed !important'
39694                             },
39695                             '.course-timesheet .course-item' : {
39696                                 'font-family'   : 'tahoma, arial, helvetica',
39697                                 'font-size'     : '11px',
39698                                 'overflow'      : 'hidden',
39699                                 'padding-left'  : '10px',
39700                                 'padding-right' : '10px',
39701                                 'padding-top' : '10px' 
39702                             }
39703                             
39704                         }, Roo.id());
39705                                 this.ds.load({});
39706                     }
39707                 },
39708                 autoWidth : true,
39709                 monitorWindowResize : false,
39710                 cellrenderer : function(v,x,r)
39711                 {
39712                     return v;
39713                 },
39714                 sm : {
39715                     xtype: 'CellSelectionModel',
39716                     xns: Roo.grid
39717                 },
39718                 dataSource : {
39719                     xtype: 'Store',
39720                     xns: Roo.data,
39721                     listeners : {
39722                         beforeload : function (_self, options)
39723                         {
39724                             options.params = options.params || {};
39725                             options.params._month = _this.monthField.getValue();
39726                             options.params.limit = 9999;
39727                             options.params['sort'] = 'when_dt';    
39728                             options.params['dir'] = 'ASC';    
39729                             this.proxy.loadResponse = this.loadResponse;
39730                             Roo.log("load?");
39731                             //this.addColumns();
39732                         },
39733                         load : function (_self, records, options)
39734                         {
39735                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
39736                                 // if you click on the translation.. you can edit it...
39737                                 var el = Roo.get(this);
39738                                 var id = el.dom.getAttribute('data-id');
39739                                 var d = el.dom.getAttribute('data-date');
39740                                 var t = el.dom.getAttribute('data-time');
39741                                 //var id = this.child('span').dom.textContent;
39742                                 
39743                                 //Roo.log(this);
39744                                 Pman.Dialog.CourseCalendar.show({
39745                                     id : id,
39746                                     when_d : d,
39747                                     when_t : t,
39748                                     productitem_active : id ? 1 : 0
39749                                 }, function() {
39750                                     _this.grid.ds.load({});
39751                                 });
39752                            
39753                            });
39754                            
39755                            _this.panel.fireEvent('resize', [ '', '' ]);
39756                         }
39757                     },
39758                     loadResponse : function(o, success, response){
39759                             // this is overridden on before load..
39760                             
39761                             Roo.log("our code?");       
39762                             //Roo.log(success);
39763                             //Roo.log(response)
39764                             delete this.activeRequest;
39765                             if(!success){
39766                                 this.fireEvent("loadexception", this, o, response);
39767                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39768                                 return;
39769                             }
39770                             var result;
39771                             try {
39772                                 result = o.reader.read(response);
39773                             }catch(e){
39774                                 Roo.log("load exception?");
39775                                 this.fireEvent("loadexception", this, o, response, e);
39776                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
39777                                 return;
39778                             }
39779                             Roo.log("ready...");        
39780                             // loop through result.records;
39781                             // and set this.tdate[date] = [] << array of records..
39782                             _this.tdata  = {};
39783                             Roo.each(result.records, function(r){
39784                                 //Roo.log(r.data);
39785                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
39786                                     _this.tdata[r.data.when_dt.format('j')] = [];
39787                                 }
39788                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
39789                             });
39790                             
39791                             //Roo.log(_this.tdata);
39792                             
39793                             result.records = [];
39794                             result.totalRecords = 6;
39795                     
39796                             // let's generate some duumy records for the rows.
39797                             //var st = _this.dateField.getValue();
39798                             
39799                             // work out monday..
39800                             //st = st.add(Date.DAY, -1 * st.format('w'));
39801                             
39802                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39803                             
39804                             var firstOfMonth = date.getFirstDayOfMonth();
39805                             var days = date.getDaysInMonth();
39806                             var d = 1;
39807                             var firstAdded = false;
39808                             for (var i = 0; i < result.totalRecords ; i++) {
39809                                 //var d= st.add(Date.DAY, i);
39810                                 var row = {};
39811                                 var added = 0;
39812                                 for(var w = 0 ; w < 7 ; w++){
39813                                     if(!firstAdded && firstOfMonth != w){
39814                                         continue;
39815                                     }
39816                                     if(d > days){
39817                                         continue;
39818                                     }
39819                                     firstAdded = true;
39820                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
39821                                     row['weekday'+w] = String.format(
39822                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
39823                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
39824                                                     d,
39825                                                     date.format('Y-m-')+dd
39826                                                 );
39827                                     added++;
39828                                     if(typeof(_this.tdata[d]) != 'undefined'){
39829                                         Roo.each(_this.tdata[d], function(r){
39830                                             var is_sub = '';
39831                                             var deactive = '';
39832                                             var id = r.id;
39833                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
39834                                             if(r.parent_id*1>0){
39835                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
39836                                                 id = r.parent_id;
39837                                             }
39838                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
39839                                                 deactive = 'de-act-link';
39840                                             }
39841                                             
39842                                             row['weekday'+w] += String.format(
39843                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
39844                                                     id, //0
39845                                                     r.product_id_name, //1
39846                                                     r.when_dt.format('h:ia'), //2
39847                                                     is_sub, //3
39848                                                     deactive, //4
39849                                                     desc // 5
39850                                             );
39851                                         });
39852                                     }
39853                                     d++;
39854                                 }
39855                                 
39856                                 // only do this if something added..
39857                                 if(added > 0){ 
39858                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
39859                                 }
39860                                 
39861                                 
39862                                 // push it twice. (second one with an hour..
39863                                 
39864                             }
39865                             //Roo.log(result);
39866                             this.fireEvent("load", this, o, o.request.arg);
39867                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
39868                         },
39869                     sortInfo : {field: 'when_dt', direction : 'ASC' },
39870                     proxy : {
39871                         xtype: 'HttpProxy',
39872                         xns: Roo.data,
39873                         method : 'GET',
39874                         url : baseURL + '/Roo/Shop_course.php'
39875                     },
39876                     reader : {
39877                         xtype: 'JsonReader',
39878                         xns: Roo.data,
39879                         id : 'id',
39880                         fields : [
39881                             {
39882                                 'name': 'id',
39883                                 'type': 'int'
39884                             },
39885                             {
39886                                 'name': 'when_dt',
39887                                 'type': 'string'
39888                             },
39889                             {
39890                                 'name': 'end_dt',
39891                                 'type': 'string'
39892                             },
39893                             {
39894                                 'name': 'parent_id',
39895                                 'type': 'int'
39896                             },
39897                             {
39898                                 'name': 'product_id',
39899                                 'type': 'int'
39900                             },
39901                             {
39902                                 'name': 'productitem_id',
39903                                 'type': 'int'
39904                             },
39905                             {
39906                                 'name': 'guid',
39907                                 'type': 'int'
39908                             }
39909                         ]
39910                     }
39911                 },
39912                 toolbar : {
39913                     xtype: 'Toolbar',
39914                     xns: Roo,
39915                     items : [
39916                         {
39917                             xtype: 'Button',
39918                             xns: Roo.Toolbar,
39919                             listeners : {
39920                                 click : function (_self, e)
39921                                 {
39922                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39923                                     sd.setMonth(sd.getMonth()-1);
39924                                     _this.monthField.setValue(sd.format('Y-m-d'));
39925                                     _this.grid.ds.load({});
39926                                 }
39927                             },
39928                             text : "Back"
39929                         },
39930                         {
39931                             xtype: 'Separator',
39932                             xns: Roo.Toolbar
39933                         },
39934                         {
39935                             xtype: 'MonthField',
39936                             xns: Roo.form,
39937                             listeners : {
39938                                 render : function (_self)
39939                                 {
39940                                     _this.monthField = _self;
39941                                    // _this.monthField.set  today
39942                                 },
39943                                 select : function (combo, date)
39944                                 {
39945                                     _this.grid.ds.load({});
39946                                 }
39947                             },
39948                             value : (function() { return new Date(); })()
39949                         },
39950                         {
39951                             xtype: 'Separator',
39952                             xns: Roo.Toolbar
39953                         },
39954                         {
39955                             xtype: 'TextItem',
39956                             xns: Roo.Toolbar,
39957                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
39958                         },
39959                         {
39960                             xtype: 'Fill',
39961                             xns: Roo.Toolbar
39962                         },
39963                         {
39964                             xtype: 'Button',
39965                             xns: Roo.Toolbar,
39966                             listeners : {
39967                                 click : function (_self, e)
39968                                 {
39969                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
39970                                     sd.setMonth(sd.getMonth()+1);
39971                                     _this.monthField.setValue(sd.format('Y-m-d'));
39972                                     _this.grid.ds.load({});
39973                                 }
39974                             },
39975                             text : "Next"
39976                         }
39977                     ]
39978                 },
39979                  
39980             }
39981         };
39982         
39983         *//*
39984  * Based on:
39985  * Ext JS Library 1.1.1
39986  * Copyright(c) 2006-2007, Ext JS, LLC.
39987  *
39988  * Originally Released Under LGPL - original licence link has changed is not relivant.
39989  *
39990  * Fork - LGPL
39991  * <script type="text/javascript">
39992  */
39993  
39994 /**
39995  * @class Roo.LoadMask
39996  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39997  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39998  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39999  * element's UpdateManager load indicator and will be destroyed after the initial load.
40000  * @constructor
40001  * Create a new LoadMask
40002  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40003  * @param {Object} config The config object
40004  */
40005 Roo.LoadMask = function(el, config){
40006     this.el = Roo.get(el);
40007     Roo.apply(this, config);
40008     if(this.store){
40009         this.store.on('beforeload', this.onBeforeLoad, this);
40010         this.store.on('load', this.onLoad, this);
40011         this.store.on('loadexception', this.onLoadException, this);
40012         this.removeMask = false;
40013     }else{
40014         var um = this.el.getUpdateManager();
40015         um.showLoadIndicator = false; // disable the default indicator
40016         um.on('beforeupdate', this.onBeforeLoad, this);
40017         um.on('update', this.onLoad, this);
40018         um.on('failure', this.onLoad, this);
40019         this.removeMask = true;
40020     }
40021 };
40022
40023 Roo.LoadMask.prototype = {
40024     /**
40025      * @cfg {Boolean} removeMask
40026      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40027      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40028      */
40029     removeMask : false,
40030     /**
40031      * @cfg {String} msg
40032      * The text to display in a centered loading message box (defaults to 'Loading...')
40033      */
40034     msg : 'Loading...',
40035     /**
40036      * @cfg {String} msgCls
40037      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40038      */
40039     msgCls : 'x-mask-loading',
40040
40041     /**
40042      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40043      * @type Boolean
40044      */
40045     disabled: false,
40046
40047     /**
40048      * Disables the mask to prevent it from being displayed
40049      */
40050     disable : function(){
40051        this.disabled = true;
40052     },
40053
40054     /**
40055      * Enables the mask so that it can be displayed
40056      */
40057     enable : function(){
40058         this.disabled = false;
40059     },
40060     
40061     onLoadException : function()
40062     {
40063         Roo.log(arguments);
40064         
40065         if (typeof(arguments[3]) != 'undefined') {
40066             Roo.MessageBox.alert("Error loading",arguments[3]);
40067         } 
40068         /*
40069         try {
40070             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40071                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40072             }   
40073         } catch(e) {
40074             
40075         }
40076         */
40077     
40078         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40079     },
40080     // private
40081     onLoad : function()
40082     {
40083         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
40084     },
40085
40086     // private
40087     onBeforeLoad : function(){
40088         if(!this.disabled){
40089             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
40090         }
40091     },
40092
40093     // private
40094     destroy : function(){
40095         if(this.store){
40096             this.store.un('beforeload', this.onBeforeLoad, this);
40097             this.store.un('load', this.onLoad, this);
40098             this.store.un('loadexception', this.onLoadException, this);
40099         }else{
40100             var um = this.el.getUpdateManager();
40101             um.un('beforeupdate', this.onBeforeLoad, this);
40102             um.un('update', this.onLoad, this);
40103             um.un('failure', this.onLoad, this);
40104         }
40105     }
40106 };/*
40107  * Based on:
40108  * Ext JS Library 1.1.1
40109  * Copyright(c) 2006-2007, Ext JS, LLC.
40110  *
40111  * Originally Released Under LGPL - original licence link has changed is not relivant.
40112  *
40113  * Fork - LGPL
40114  * <script type="text/javascript">
40115  */
40116
40117
40118 /**
40119  * @class Roo.XTemplate
40120  * @extends Roo.Template
40121  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40122 <pre><code>
40123 var t = new Roo.XTemplate(
40124         '&lt;select name="{name}"&gt;',
40125                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40126         '&lt;/select&gt;'
40127 );
40128  
40129 // then append, applying the master template values
40130  </code></pre>
40131  *
40132  * Supported features:
40133  *
40134  *  Tags:
40135
40136 <pre><code>
40137       {a_variable} - output encoded.
40138       {a_variable.format:("Y-m-d")} - call a method on the variable
40139       {a_variable:raw} - unencoded output
40140       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40141       {a_variable:this.method_on_template(...)} - call a method on the template object.
40142  
40143 </code></pre>
40144  *  The tpl tag:
40145 <pre><code>
40146         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40147         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40148         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40149         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40150   
40151         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40152         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40153 </code></pre>
40154  *      
40155  */
40156 Roo.XTemplate = function()
40157 {
40158     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40159     if (this.html) {
40160         this.compile();
40161     }
40162 };
40163
40164
40165 Roo.extend(Roo.XTemplate, Roo.Template, {
40166
40167     /**
40168      * The various sub templates
40169      */
40170     tpls : false,
40171     /**
40172      *
40173      * basic tag replacing syntax
40174      * WORD:WORD()
40175      *
40176      * // you can fake an object call by doing this
40177      *  x.t:(test,tesT) 
40178      * 
40179      */
40180     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40181
40182     /**
40183      * compile the template
40184      *
40185      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40186      *
40187      */
40188     compile: function()
40189     {
40190         var s = this.html;
40191      
40192         s = ['<tpl>', s, '</tpl>'].join('');
40193     
40194         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40195             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40196             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40197             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40198             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40199             m,
40200             id     = 0,
40201             tpls   = [];
40202     
40203         while(true == !!(m = s.match(re))){
40204             var forMatch   = m[0].match(nameRe),
40205                 ifMatch   = m[0].match(ifRe),
40206                 execMatch   = m[0].match(execRe),
40207                 namedMatch   = m[0].match(namedRe),
40208                 
40209                 exp  = null, 
40210                 fn   = null,
40211                 exec = null,
40212                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40213                 
40214             if (ifMatch) {
40215                 // if - puts fn into test..
40216                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40217                 if(exp){
40218                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40219                 }
40220             }
40221             
40222             if (execMatch) {
40223                 // exec - calls a function... returns empty if true is  returned.
40224                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40225                 if(exp){
40226                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40227                 }
40228             }
40229             
40230             
40231             if (name) {
40232                 // for = 
40233                 switch(name){
40234                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40235                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40236                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40237                 }
40238             }
40239             var uid = namedMatch ? namedMatch[1] : id;
40240             
40241             
40242             tpls.push({
40243                 id:     namedMatch ? namedMatch[1] : id,
40244                 target: name,
40245                 exec:   exec,
40246                 test:   fn,
40247                 body:   m[1] || ''
40248             });
40249             if (namedMatch) {
40250                 s = s.replace(m[0], '');
40251             } else { 
40252                 s = s.replace(m[0], '{xtpl'+ id + '}');
40253             }
40254             ++id;
40255         }
40256         this.tpls = [];
40257         for(var i = tpls.length-1; i >= 0; --i){
40258             this.compileTpl(tpls[i]);
40259             this.tpls[tpls[i].id] = tpls[i];
40260         }
40261         this.master = tpls[tpls.length-1];
40262         return this;
40263     },
40264     /**
40265      * same as applyTemplate, except it's done to one of the subTemplates
40266      * when using named templates, you can do:
40267      *
40268      * var str = pl.applySubTemplate('your-name', values);
40269      *
40270      * 
40271      * @param {Number} id of the template
40272      * @param {Object} values to apply to template
40273      * @param {Object} parent (normaly the instance of this object)
40274      */
40275     applySubTemplate : function(id, values, parent)
40276     {
40277         
40278         
40279         var t = this.tpls[id];
40280         
40281         
40282         try { 
40283             if(t.test && !t.test.call(this, values, parent)){
40284                 return '';
40285             }
40286         } catch(e) {
40287             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40288             Roo.log(e.toString());
40289             Roo.log(t.test);
40290             return ''
40291         }
40292         try { 
40293             
40294             if(t.exec && t.exec.call(this, values, parent)){
40295                 return '';
40296             }
40297         } catch(e) {
40298             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40299             Roo.log(e.toString());
40300             Roo.log(t.exec);
40301             return ''
40302         }
40303         try {
40304             var vs = t.target ? t.target.call(this, values, parent) : values;
40305             parent = t.target ? values : parent;
40306             if(t.target && vs instanceof Array){
40307                 var buf = [];
40308                 for(var i = 0, len = vs.length; i < len; i++){
40309                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40310                 }
40311                 return buf.join('');
40312             }
40313             return t.compiled.call(this, vs, parent);
40314         } catch (e) {
40315             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40316             Roo.log(e.toString());
40317             Roo.log(t.compiled);
40318             return '';
40319         }
40320     },
40321
40322     compileTpl : function(tpl)
40323     {
40324         var fm = Roo.util.Format;
40325         var useF = this.disableFormats !== true;
40326         var sep = Roo.isGecko ? "+" : ",";
40327         var undef = function(str) {
40328             Roo.log("Property not found :"  + str);
40329             return '';
40330         };
40331         
40332         var fn = function(m, name, format, args)
40333         {
40334             //Roo.log(arguments);
40335             args = args ? args.replace(/\\'/g,"'") : args;
40336             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40337             if (typeof(format) == 'undefined') {
40338                 format= 'htmlEncode';
40339             }
40340             if (format == 'raw' ) {
40341                 format = false;
40342             }
40343             
40344             if(name.substr(0, 4) == 'xtpl'){
40345                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40346             }
40347             
40348             // build an array of options to determine if value is undefined..
40349             
40350             // basically get 'xxxx.yyyy' then do
40351             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40352             //    (function () { Roo.log("Property not found"); return ''; })() :
40353             //    ......
40354             
40355             var udef_ar = [];
40356             var lookfor = '';
40357             Roo.each(name.split('.'), function(st) {
40358                 lookfor += (lookfor.length ? '.': '') + st;
40359                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40360             });
40361             
40362             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40363             
40364             
40365             if(format && useF){
40366                 
40367                 args = args ? ',' + args : "";
40368                  
40369                 if(format.substr(0, 5) != "this."){
40370                     format = "fm." + format + '(';
40371                 }else{
40372                     format = 'this.call("'+ format.substr(5) + '", ';
40373                     args = ", values";
40374                 }
40375                 
40376                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40377             }
40378              
40379             if (args.length) {
40380                 // called with xxyx.yuu:(test,test)
40381                 // change to ()
40382                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40383             }
40384             // raw.. - :raw modifier..
40385             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40386             
40387         };
40388         var body;
40389         // branched to use + in gecko and [].join() in others
40390         if(Roo.isGecko){
40391             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40392                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40393                     "';};};";
40394         }else{
40395             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40396             body.push(tpl.body.replace(/(\r\n|\n)/g,
40397                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40398             body.push("'].join('');};};");
40399             body = body.join('');
40400         }
40401         
40402         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40403        
40404         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40405         eval(body);
40406         
40407         return this;
40408     },
40409
40410     applyTemplate : function(values){
40411         return this.master.compiled.call(this, values, {});
40412         //var s = this.subs;
40413     },
40414
40415     apply : function(){
40416         return this.applyTemplate.apply(this, arguments);
40417     }
40418
40419  });
40420
40421 Roo.XTemplate.from = function(el){
40422     el = Roo.getDom(el);
40423     return new Roo.XTemplate(el.value || el.innerHTML);
40424 };